Browse Source

MMGen version 0.8.3

New features/improvements:

	* New native Bitcoin RPC library.
	* Support for cookie-based RPC authentication (new in Bitcoin Core v0.12.0).
	* Batch mode available when listing and importing addresses.
	* mmgen-tool listaddresses: 'addrs' argument allows you to specify an
	  address or range of addresses.

NOTE: if MMGen is already installed on your system, you must remove your
existing installation by hand before installing this new version.  On Linux,
this means deleting everything under the directory
'/usr/local/lib/python2.7/dist-packages/mmgen/'.  Also, if you did a 'git pull'
instead of a fresh clone, you must delete the 'build' directory in the
repository root before installing.

The 'mmgen-pywallet' utility has been removed.  It's no longer needed, as the
'bitcoin-cli dumpwallet' command (available since Core v0.9.0) provides
equivalent functionality.

The Windows port isn't being actively maintained at the moment.  Use at your own
risk, and report any problems on the Bitcointalk forum.
philemon 9 years ago
parent
commit
956eeab186
55 changed files with 2108 additions and 5138 deletions
  1. 10 11
      doc/wiki/install-linux/Install-MMGen-on-Debian-or-Ubuntu-Linux.md
  2. 44 33
      doc/wiki/using-mmgen/Getting-Started-with-MMGen.md
  3. 1 1
      mmgen-addrgen
  4. 1 1
      mmgen-addrimport
  5. 1 1
      mmgen-keygen
  6. 1 1
      mmgen-passchg
  7. 0 24
      mmgen-pywallet
  8. 1 1
      mmgen-tool
  9. 1 1
      mmgen-txcreate
  10. 1 1
      mmgen-txsend
  11. 1 1
      mmgen-txsign
  12. 1 1
      mmgen-walletchk
  13. 1 1
      mmgen-walletconv
  14. 1 1
      mmgen-walletgen
  15. 1 1
      mmgen/__init__.py
  16. 76 82
      mmgen/addr.py
  17. 21 21
      mmgen/bitcoin.py
  18. 8 3
      mmgen/common.py
  19. 59 63
      mmgen/crypto.py
  20. 2 2
      mmgen/filename.py
  21. 39 39
      mmgen/globalvars.py
  22. 2 2
      mmgen/license.py
  23. 8 8
      mmgen/main.py
  24. 25 30
      mmgen/main_addrgen.py
  25. 43 38
      mmgen/main_addrimport.py
  26. 0 1684
      mmgen/main_pywallet.py
  27. 11 15
      mmgen/main_tool.py
  28. 137 152
      mmgen/main_txcreate.py
  29. 19 24
      mmgen/main_txsend.py
  30. 76 83
      mmgen/main_txsign.py
  31. 41 42
      mmgen/main_wallet.py
  32. 1 1
      mmgen/mn_electrum.py
  33. 1 1
      mmgen/mn_tirosh.py
  34. 19 20
      mmgen/obj.py
  35. 65 71
      mmgen/opts.py
  36. 128 0
      mmgen/rpc.py
  37. 0 53
      mmgen/rpc/__init__.py
  38. 0 75
      mmgen/rpc/config.py
  39. 0 766
      mmgen/rpc/connection.py
  40. 0 168
      mmgen/rpc/data.py
  41. 0 203
      mmgen/rpc/exceptions.py
  42. 0 142
      mmgen/rpc/proxy.py
  43. 0 49
      mmgen/rpc/util.py
  44. 202 236
      mmgen/seed.py
  45. 36 32
      mmgen/share/Opts.py
  46. 1 1
      mmgen/share/__init__.py
  47. 29 30
      mmgen/term.py
  48. 8 8
      mmgen/test.py
  49. 171 174
      mmgen/tool.py
  50. 99 102
      mmgen/tx.py
  51. 189 178
      mmgen/util.py
  52. 6 15
      setup.py
  53. 35 22
      test/gentest.py
  54. 332 319
      test/test.py
  55. 153 104
      test/tooltest.py

+ 10 - 11
doc/wiki/install-linux/Install-MMGen-on-Debian-or-Ubuntu-Linux.md

@@ -12,17 +12,16 @@ Install the pexpect Python module:
 
 
 		$ sudo pip install pexpect
 		$ sudo pip install pexpect
 
 
-		Note: pexpect v4.0.1 (the latest version as of this writing) is
-		BROKEN and will cause errors when running the test suite!
-		If this is the version you just installed on your system (examine the
-		output of 'pip freeze' to find out), then you must downgrade.  Note
-		that newer versions may be broken as well.  Version 3.1 is known to work
-		and can be found [here][03].  First, uninstall the broken pexpect:
-
-		$ sudo pip uninstall pexpect
-
-		Then download and unpack the v3.1 tarball, cd to the archive root and
-		run 'sudo python setup.py install' to install it.
+>> Note: pexpect v4.0.1 (the latest version as of this writing) is BROKEN and
+>> will cause errors when running the test suite!  If this is the version you
+>> just installed on your system (examine the output of 'pip freeze' to find
+>> out), then you must downgrade.  Note that newer versions may be broken as
+>> well.  Version 3.1 is known to work.  If this is the version on your system,
+>> then you may skip the next step.  Otherwise, download the [v3.1 tarball][03],
+>> unpack it, cd to the archive root and run:
+
+			$ sudo pip uninstall pexpect
+			$ sudo python setup.py install
 
 
 Install MMGen:
 Install MMGen:
 
 

+ 44 - 33
doc/wiki/using-mmgen/Getting-Started-with-MMGen.md

@@ -229,55 +229,66 @@ confirmations, your transaction will be saved:
 Note that the transaction has a unique ID, and the non-change spend amount of
 Note that the transaction has a unique ID, and the non-change spend amount of
 6.6 BTC is included in the filename.
 6.6 BTC is included in the filename.
 
 
-#### <a name=06>Sign a transaction (offline computer):</a>
+#### <a name=06>Create a keylist file (online computer):</a>
 
 
-Now copy the raw transaction you've just created to a USB stick and transfer it
-to your offline computer for signing.  For this you'll need the key corresponding
-to the transaction's input address, 1F93Znz....  If the key in question is in a
-bitcoind 'wallet.dat', copy 'wallet.dat' to your offline machine as well and use
-the included command 'mmgen-pywallet' (a modified version of the well-known
-pywallet utility) to extract the required key from it.
+To sign your transaction, you'll need the Bitcoin private key corresponding to
+its input address, '1F93Znz....'
 
 
-		$ mmgen-pywallet -k wallet.dat
-		...
-		Private keys written to file 'wd_EDBC983A[102].keys'
+If the key in question is in a bitcoind wallet ('wallet.dat'), you'll want to
+extract the key (along with all the other keys in the wallet) to a keylist
+file.  This is done using the 'bitcoin-cli dumpwallet' command with bitcoind
+running.
+
+		$ bitcoin-cli dumpwallet my_secret.keys
+
+This will write the keylist file 'my_secret.keys' (or whatever filename you've
+chosen) to your home directory (or maybe to your Bitcoin data directory, results
+may vary).  If you want it written to another location, provide an absolute
+path.
+
+Note that the keylist file lists your private keys in *unencrypted* form, even
+if your 'wallet.dat' was encrypted.  Therefore, it should be backed up to a safe
+location—to a USB stick, say, or to your offline computer.  After you've backed
+it up, securely delete all copies of it on your online computer.
 
 
-You've in fact extracted a list of all of the wallet's 102 keys here, but that's
-not a problem, since the unused keys will be ignored (if you wish, you can
-extract only the one key you need using the '--keys-for-addrs' option).  Now go
-ahead and sign the transaction using this key list:
+You'll use this keylist file to sign all transactions that spend from addresses
+in your bitcoind wallet.
 
 
-		$ mmgen-txsign -k wd_EDBC983A[102].keys tx_FEDCBA[6.6].raw
+If the key/address pair in question came from another source, you may create
+your own file 'my_secret.keys' (or whatever) in a plain text editor and just
+list the key in this file.  In the case of multiple keys, just list them all,
+one key per line.  In our example, the file will have one line containing a
+single private key corresponding to the address '1F93Znz....'
+
+#### <a name=06>Sign a transaction (offline computer):</a>
+
+Now transfer the the raw transaction file and just-created keylist file to your
+offline computer and run:
+
+		$ mmgen-txsign -k my_secret.keys tx_FEDCBA[6.6].raw
 		...
 		...
 		Signed transaction written to file 'tx_FEDCBA[6.6].sig'
 		Signed transaction written to file 'tx_FEDCBA[6.6].sig'
 
 
-Note that 'mmgen-pywallet's output is just a flat list of keys.  So if you have
-several Bitcoin wallets with balances, you can just concatenate these lists into
-a single file which you can use to sign all future transactions with
-'wallet.dat' inputs:
-
-		$ mmgen-pywallet -k wallet1.dat
-		$ mmgen-pywallet -k wallet2.dat
-		$ mmgen-pywallet -k wallet3.dat
-		$ cat wd_*.keys > all_keys
+The signed transaction is written to a new file whose name differs from the raw
+transaction file only by its '.sig' extension.
 
 
-Once you've migrated your funds to MMGen, such key files will no longer be
+NOTE: once you've migrated your funds to MMGen, the keylist file will no longer be
 needed.  Instead, you'll sign transactions by listing an MMGen seed source
 needed.  Instead, you'll sign transactions by listing an MMGen seed source
-(wallet, mnemonic or seed file) on the command line after the transaction,
-and the required keys will be generated on the fly, as in this example:
+(wallet, mnemonic or seed file) on the command line after the transaction, and
+the required keys will be generated on the fly, as in the following example:
 
 
-		$ mmgen-txsign tx_ABCDE[1.2345].raw 1234567A-BCDEF123[256,3].mmdat
+		$ mmgen-txsign tx_ABCDE[1].raw my_mmgen_wallet.mmdat
 
 
-Transactions may contain a mixture of MMGen and non-MMGen inputs, as well as
-inputs with more than one MMGen Seed ID.  Just list a seed source for each
+NOTE: transactions may contain a mixture of MMGen and non-MMGen inputs, as well
+as inputs with more than one MMGen Seed ID.  Just list a seed source for each
 MMGen input on the command line after the transaction, as in this example:
 MMGen input on the command line after the transaction, as in this example:
 
 
-		$ mmgen-txsign -k key_list my_tx.raw a.mmdat b.mmwords c.mmseed
+		$ mmgen-txsign -k my_secret.keys my_tx.raw a.mmdat b.mmwords c.mmseed
 
 
 #### <a name=07>Send a transaction (online computer):</a>
 #### <a name=07>Send a transaction (online computer):</a>
 
 
 Now you're ready for the final step: broadcasting the transaction to the
 Now you're ready for the final step: broadcasting the transaction to the
-network.  Copy just-created signed transaction file to your online computer,
+network.  Copy the just-created signed transaction file to your online computer,
 start bitcoind and issue the command:
 start bitcoind and issue the command:
 
 
 		$ mmgen-txsend tx_FEDCBA[6.6].sig
 		$ mmgen-txsend tx_FEDCBA[6.6].sig
@@ -301,7 +312,7 @@ Your total MMGen balance will also now be visible:
 		89ABCDEF:     0 BTC            0 BTC            9.99995 BTC
 		89ABCDEF:     0 BTC            0 BTC            9.99995 BTC
 		TOTAL:        0 BTC            0 BTC            9.99995 BTC
 		TOTAL:        0 BTC            0 BTC            9.99995 BTC
 
 
-To verify that your transaction's received its first, second, third and so forth
+To verify that your transaction's received its first, second, third, and so on,
 confirmation, increase the 'minconf' value to 1, 2, 3 and so forth.
 confirmation, increase the 'minconf' value to 1, 2, 3 and so forth.
 
 
 Congratulations!  You've performed your first MMGen transaction and placed your
 Congratulations!  You've performed your first MMGen transaction and placed your

+ 1 - 1
mmgen-addrgen

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-addrimport

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-keygen

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-passchg

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 0 - 24
mmgen-pywallet

@@ -1,24 +0,0 @@
-#!/usr/bin/env python
-
-# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-mmgen-pywallet: Dump contents of a bitcoind wallet to file
-"""
-
-from mmgen.main import launch
-launch("pywallet")

+ 1 - 1
mmgen-tool

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-txcreate

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-txsend

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-txsign

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-walletchk

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-walletconv

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen-walletgen

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen/__init__.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 76 - 82
mmgen/addr.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,17 +20,13 @@
 addr.py:  Address generation/display routines for the MMGen suite
 addr.py:  Address generation/display routines for the MMGen suite
 """
 """
 
 
-import sys
-from hashlib import sha256, sha512
-from hashlib import new as hashlib_new
+from hashlib import sha256, sha512, new as hashlib_new
 from binascii import hexlify, unhexlify
 from binascii import hexlify, unhexlify
 
 
+from mmgen.common import *
 from mmgen.bitcoin import numtowif
 from mmgen.bitcoin import numtowif
-from mmgen.util import *
 from mmgen.tx import *
 from mmgen.tx import *
 from mmgen.obj import *
 from mmgen.obj import *
-import mmgen.globalvars as g
-import mmgen.opt as opt
 
 
 pnm = g.proj_name
 pnm = g.proj_name
 
 
@@ -54,7 +50,7 @@ addrmsgs = {
 Executable '{kconv}' unavailable. Falling back on (slow) internal ECDSA library.
 Executable '{kconv}' unavailable. Falling back on (slow) internal ECDSA library.
 Please install '{kconv}' from the {vgen} package on your system for much
 Please install '{kconv}' from the {vgen} package on your system for much
 faster address generation.
 faster address generation.
-""".format(kconv=g.keyconv_exec, vgen="vanitygen")
+""".format(kconv=g.keyconv_exec, vgen='vanitygen')
 }
 }
 
 
 def test_for_keyconv(silent=False):
 def test_for_keyconv(silent=False):
@@ -69,19 +65,19 @@ def test_for_keyconv(silent=False):
 	return True
 	return True
 
 
 
 
-def generate_addrs(seed, addrnums, source="addrgen"):
+def generate_addrs(seed, addrnums, source='addrgen'):
 
 
 	from util import make_chksum_8
 	from util import make_chksum_8
 	seed_id = make_chksum_8(seed) # Must do this before seed gets clobbered
 	seed_id = make_chksum_8(seed) # Must do this before seed gets clobbered
 
 
 	if 'a' in opt.gen_what:
 	if 'a' in opt.gen_what:
 		if opt.no_keyconv or test_for_keyconv() == False:
 		if opt.no_keyconv or test_for_keyconv() == False:
-			msg("Using (slow) internal ECDSA library for address generation")
+			msg('Using (slow) internal ECDSA library for address generation')
 			from mmgen.bitcoin import privnum2addr
 			from mmgen.bitcoin import privnum2addr
 			keyconv = False
 			keyconv = False
 		else:
 		else:
 			from subprocess import check_output
 			from subprocess import check_output
-			keyconv = "keyconv"
+			keyconv = 'keyconv'
 
 
 	addrnums = sorted(set(addrnums)) # don't trust the calling function
 	addrnums = sorted(set(addrnums)) # don't trust the calling function
 	t_addrs,num,pos,out = len(addrnums),0,0,[]
 	t_addrs,num,pos,out = len(addrnums),0,0,[]
@@ -102,7 +98,7 @@ def generate_addrs(seed, addrnums, source="addrgen"):
 
 
 		pos += 1
 		pos += 1
 
 
-		qmsg_r("\rGenerating %s #%s (%s of %s)" % (w[0],num,pos,t_addrs))
+		qmsg_r('\rGenerating %s #%s (%s of %s)' % (w[0],num,pos,t_addrs))
 
 
 		e = AddrInfoEntry()
 		e = AddrInfoEntry()
 		e.idx = num
 		e.idx = num
@@ -123,7 +119,7 @@ def generate_addrs(seed, addrnums, source="addrgen"):
 		out.append(e)
 		out.append(e)
 
 
 	m = w[0] if t_addrs == 1 else w[0]+w[1]
 	m = w[0] if t_addrs == 1 else w[0]+w[1]
-	qmsg("\r%s: %s %s generated%s" % (seed_id,t_addrs,m," "*15))
+	qmsg('\r%s: %s %s generated%s' % (seed_id,t_addrs,m,' '*15))
 	a = AddrInfo(has_keys='k' in opt.gen_what, source=source)
 	a = AddrInfo(has_keys='k' in opt.gen_what, source=source)
 	a.initialize(seed_id,out)
 	a.initialize(seed_id,out)
 	return a
 	return a
@@ -131,7 +127,7 @@ def generate_addrs(seed, addrnums, source="addrgen"):
 def _parse_addrfile_body(lines,has_keys=False,check=False):
 def _parse_addrfile_body(lines,has_keys=False,check=False):
 
 
 	if has_keys and len(lines) % 2:
 	if has_keys and len(lines) % 2:
-		return "Key-address file has odd number of lines"
+		return 'Key-address file has odd number of lines'
 
 
 	ret = []
 	ret = []
 	while lines:
 	while lines:
@@ -145,7 +141,7 @@ def _parse_addrfile_body(lines,has_keys=False,check=False):
 			return "'%s': invalid Bitcoin address" % d[1]
 			return "'%s': invalid Bitcoin address" % d[1]
 
 
 		if len(d) == 3: check_addr_label(d[2])
 		if len(d) == 3: check_addr_label(d[2])
-		else:           d.append("")
+		else:           d.append('')
 
 
 		a.idx,a.addr,a.comment = int(d[0]),unicode(d[1]),unicode(d[2])
 		a.idx,a.addr,a.comment = int(d[0]),unicode(d[1]),unicode(d[2])
 
 
@@ -153,7 +149,7 @@ def _parse_addrfile_body(lines,has_keys=False,check=False):
 			l = lines.pop(0)
 			l = lines.pop(0)
 			d = l.split(None,2)
 			d = l.split(None,2)
 
 
-			if d[0] != "wif:":
+			if d[0] != 'wif:':
 				return "Invalid key line in file: '%s'" % l
 				return "Invalid key line in file: '%s'" % l
 			if not is_wif(d[1]):
 			if not is_wif(d[1]):
 				return "'%s': invalid Bitcoin key" % d[1]
 				return "'%s': invalid Bitcoin key" % d[1]
@@ -162,14 +158,14 @@ def _parse_addrfile_body(lines,has_keys=False,check=False):
 
 
 		ret.append(a)
 		ret.append(a)
 
 
-	if has_keys and keypress_confirm("Check key-to-address validity?"):
+	if has_keys and keypress_confirm('Check key-to-address validity?'):
 		wif2addr_f = get_wif2addr_f()
 		wif2addr_f = get_wif2addr_f()
 		llen = len(ret)
 		llen = len(ret)
 		for n,e in enumerate(ret):
 		for n,e in enumerate(ret):
-			msg_r("\rVerifying keys %s/%s" % (n+1,llen))
+			msg_r('\rVerifying keys %s/%s' % (n+1,llen))
 			if e.addr != wif2addr_f(e.wif):
 			if e.addr != wif2addr_f(e.wif):
 				return "Key doesn't match address!\n  %s\n  %s" % (e.wif,e.addr)
 				return "Key doesn't match address!\n  %s\n  %s" % (e.wif,e.addr)
-		msg(" - done")
+		msg(' - done')
 
 
 	return ret
 	return ret
 
 
@@ -177,7 +173,7 @@ def _parse_addrfile_body(lines,has_keys=False,check=False):
 def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 
 
 	if buf: lines = remove_comments(buf.splitlines()) # DOS-safe
 	if buf: lines = remove_comments(buf.splitlines()) # DOS-safe
-	else:   lines = get_lines_from_file(fn,"address data",trim_comments=True)
+	else:   lines = get_lines_from_file(fn,'address data',trim_comments=True)
 
 
 	try:
 	try:
 		sid,obrace = lines[0].split()
 		sid,obrace = lines[0].split()
@@ -196,17 +192,14 @@ def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 			if type(ret) == list: return sid,ret
 			if type(ret) == list: return sid,ret
 			else: errmsg = ret
 			else: errmsg = ret
 
 
-	if exit_on_error:
-		msg(errmsg)
-		sys.exit(3)
-	else:
-		return False
+	if exit_on_error: die(3,errmsg)
+	else:             return False
 
 
 
 
 def _parse_keyaddr_file(fn):
 def _parse_keyaddr_file(fn):
 	from mmgen.crypto import mmgen_decrypt_file_maybe
 	from mmgen.crypto import mmgen_decrypt_file_maybe
-	d = mmgen_decrypt_file_maybe(fn,"key-address file")
-	return _parse_addrfile("",buf=d,has_keys=True,exit_on_error=False)
+	d = mmgen_decrypt_file_maybe(fn,'key-address file')
+	return _parse_addrfile('',buf=d,has_keys=True,exit_on_error=False)
 
 
 
 
 class AddrInfoList(MMGenObject):
 class AddrInfoList(MMGenObject):
@@ -225,31 +218,31 @@ class AddrInfoList(MMGenObject):
 			return self.data[sid]
 			return self.data[sid]
 
 
 	def mmaddr2btcaddr(self,mmaddr):
 	def mmaddr2btcaddr(self,mmaddr):
-		btcaddr = ""
-		sid,idx = mmaddr.split(":")
+		btcaddr = ''
+		sid,idx = mmaddr.split(':')
 		if sid in self.seed_ids():
 		if sid in self.seed_ids():
 			btcaddr = self.addrinfo(sid).btcaddr(int(idx))
 			btcaddr = self.addrinfo(sid).btcaddr(int(idx))
 		return btcaddr
 		return btcaddr
 
 
 	def add_wallet_data(self,c):
 	def add_wallet_data(self,c):
-		vmsg_r("Getting account data from wallet...")
-		data,accts,i = {},c.listaccounts(minconf=0,includeWatchonly=True),0
+		vmsg_r('Getting account data from wallet...')
+		accts = c.listaccounts(0,True)
+		data,i = {},0
 		for acct in accts:
 		for acct in accts:
 			ma,comment = parse_mmgen_label(acct)
 			ma,comment = parse_mmgen_label(acct)
 			if ma:
 			if ma:
 				i += 1
 				i += 1
 				addrlist = c.getaddressesbyaccount(acct)
 				addrlist = c.getaddressesbyaccount(acct)
 				if len(addrlist) != 1:
 				if len(addrlist) != 1:
-					msg(wmsg['too_many_acct_addresses'] % acct)
-					sys.exit(2)
-				seed_id,idx = ma.split(":")
+					die(2,wmsg['too_many_acct_addresses'] % acct)
+				seed_id,idx = ma.split(':')
 				if seed_id not in data:
 				if seed_id not in data:
 					data[seed_id] = []
 					data[seed_id] = []
 				a = AddrInfoEntry()
 				a = AddrInfoEntry()
 				a.idx,a.addr,a.comment = \
 				a.idx,a.addr,a.comment = \
 					int(idx),unicode(addrlist[0]),unicode(comment)
 					int(idx),unicode(addrlist[0]),unicode(comment)
 				data[seed_id].append(a)
 				data[seed_id].append(a)
-		vmsg("{n} {pnm} addresses found, {m} accounts total".format(
+		vmsg('{n} {pnm} addresses found, {m} accounts total'.format(
 				n=i,pnm=pnm,m=len(accts)))
 				n=i,pnm=pnm,m=len(accts)))
 		for sid in data:
 		for sid in data:
 			self.add(AddrInfo(sid=sid,adata=data[sid]))
 			self.add(AddrInfo(sid=sid,adata=data[sid]))
@@ -259,8 +252,7 @@ class AddrInfoList(MMGenObject):
 			self.data[addrinfo.seed_id] = addrinfo
 			self.data[addrinfo.seed_id] = addrinfo
 			return True
 			return True
 		else:
 		else:
-			msg("Error: object %s is not of type AddrInfo" % repr(addrinfo))
-			sys.exit(1)
+			die(1,'Error: object %s is not of type AddrInfo' % repr(addrinfo))
 
 
 	def make_reverse_dict(self,btcaddrs):
 	def make_reverse_dict(self,btcaddrs):
 		d = {}
 		d = {}
@@ -274,53 +266,57 @@ class AddrInfoEntry(MMGenObject):
 
 
 class AddrInfo(MMGenObject):
 class AddrInfo(MMGenObject):
 
 
-	def __init__(self,addrfile="",has_keys=False,sid="",adata=[], source=""):
+	def __init__(self,addrfile='',has_keys=False,sid='',adata=[],source='',caller=''):
 		self.has_keys = has_keys
 		self.has_keys = has_keys
+		self.caller = caller
 		do_chksum = True
 		do_chksum = True
 		if addrfile:
 		if addrfile:
-			f = _parse_keyaddr_file if has_keys else _parse_addrfile
+			f = (_parse_addrfile,_parse_keyaddr_file)[bool(has_keys)]
 			sid,adata = f(addrfile)
 			sid,adata = f(addrfile)
-			self.source = "addrfile"
+			self.source = 'addrfile'
 		elif sid and adata: # data from wallet
 		elif sid and adata: # data from wallet
-			self.source = "wallet"
+			self.source = 'wallet'
 		elif sid or adata:
 		elif sid or adata:
-			die(3,"Must specify address file, or seed_id + adata")
+			die(3,'Must specify address file, or seed_id + adata')
 		else:
 		else:
-			self.source = source if source else "unknown"
+			self.source = source if source else 'unknown'
 			return
 			return
 
 
 		self.initialize(sid,adata)
 		self.initialize(sid,adata)
 
 
 	def initialize(self,seed_id,addrdata):
 	def initialize(self,seed_id,addrdata):
 		if seed_id in self.__dict__:
 		if seed_id in self.__dict__:
-			msg("Seed ID already set for object %s" % self)
+			msg('Seed ID already set for object %s' % self)
 			return False
 			return False
 		self.seed_id = seed_id
 		self.seed_id = seed_id
 		self.addrdata = addrdata
 		self.addrdata = addrdata
 		self.num_addrs = len(addrdata)
 		self.num_addrs = len(addrdata)
-		if self.source in ("wallet","txsign"):
+		if self.source in ('wallet','txsign'):
 			self.checksum = None
 			self.checksum = None
 			self.idxs_fmt = None
 			self.idxs_fmt = None
-		elif self.source == "addrgen" and opt.gen_what == "k":
+		elif self.source == 'addrgen' and opt.gen_what == 'k':
 			self.checksum = None
 			self.checksum = None
 			self.fmt_addr_idxs()
 			self.fmt_addr_idxs()
 		else: # self.source in addrfile, addrgen
 		else: # self.source in addrfile, addrgen
 			self.make_addrdata_chksum()
 			self.make_addrdata_chksum()
-			self.fmt_addr_idxs()
-			w = "key-address" if self.has_keys else "address"
-			qmsg("Checksum for %s data %s[%s]: %s" %
-					(w,self.seed_id,self.idxs_fmt,self.checksum))
-			if self.source == "addrgen":
-				qmsg(
-"Record this checksum: it will be used to verify the address file in the future")
-			elif self.source == "addrfile":
-				qmsg("Check this value against your records")
+			if self.caller == 'tool':
+				Msg(self.checksum)
+			else:
+				self.fmt_addr_idxs()
+				w = ('address','key-address')[bool(self.has_keys)]
+				qmsg('Checksum for %s data %s[%s]: %s' %
+						(w,self.seed_id,self.idxs_fmt,self.checksum))
+				if self.source == 'addrgen':
+					qmsg(
+	'Record this checksum: it will be used to verify the address file in the future')
+				elif self.source == 'addrfile':
+					qmsg('Check this value against your records')
 
 
 	def idxs(self):
 	def idxs(self):
 		return [e.idx for e in self.addrdata]
 		return [e.idx for e in self.addrdata]
 
 
 	def addrs(self):
 	def addrs(self):
-		return ["%s:%s"%(self.seed_id,e.idx) for e in self.addrdata]
+		return ['%s:%s'%(self.seed_id,e.idx) for e in self.addrdata]
 
 
 	def addrpairs(self):
 	def addrpairs(self):
 		return [(e.idx,e.addr) for e in self.addrdata]
 		return [(e.idx,e.addr) for e in self.addrdata]
@@ -355,15 +351,15 @@ class AddrInfo(MMGenObject):
 		d,b = {},btcaddrs
 		d,b = {},btcaddrs
 		for e in self.addrdata:
 		for e in self.addrdata:
 			try:
 			try:
-				d[b[b.index(e.addr)]] = ("%s:%s"%(self.seed_id,e.idx),e.comment)
+				d[b[b.index(e.addr)]] = ('%s:%s'%(self.seed_id,e.idx),e.comment)
 			except: pass
 			except: pass
 		return d
 		return d
 
 
 
 
 	def make_addrdata_chksum(self):
 	def make_addrdata_chksum(self):
-		lines=[" ".join([str(e.idx),e.addr]+([e.wif] if self.has_keys else []))
+		lines=[' '.join([str(e.idx),e.addr]+([e.wif] if self.has_keys else []))
 						for e in self.addrdata]
 						for e in self.addrdata]
-		self.checksum = make_chksum_N(" ".join(lines), nchars=24, sep=True)
+		self.checksum = make_chksum_N(' '.join(lines), nchars=24, sep=True)
 
 
 
 
 	def fmt_data(self,enable_comments=False):
 	def fmt_data(self,enable_comments=False):
@@ -377,53 +373,51 @@ class AddrInfo(MMGenObject):
 
 
 		for i,s in enumerate(status):
 		for i,s in enumerate(status):
 			if s != 0 and s != self.num_addrs:
 			if s != 0 and s != self.num_addrs:
-				msg("%s missing %s in addr data"% (self.num_addrs-s,attrs[i]))
-				sys.exit(3)
+				die(3,'%s missing %s in addr data'% (self.num_addrs-s,attrs[i]))
 
 
 		if status[0] == status[1] == 0:
 		if status[0] == status[1] == 0:
-			msg("Addr data contains neither addresses nor keys")
-			sys.exit(3)
+			die(3,'Addr data contains neither addresses nor keys')
 
 
 		# Header
 		# Header
 		out = []
 		out = []
 		from mmgen.addr import addrmsgs
 		from mmgen.addr import addrmsgs
-		k = ('addrfile_header','keyfile_header')[int(status[0]==0)]
-		out.append(addrmsgs[k]+"\n")
+		k = ('addrfile_header','keyfile_header')[status[0]==0]
+		out.append(addrmsgs[k]+'\n')
 		if self.checksum:
 		if self.checksum:
-			w = ("Key-address","Address")[int(status[1]==0)]
-			out.append("# {} data checksum for {}[{}]: {}".format(
+			w = ('Key-address','Address')[status[1]==0]
+			out.append('# {} data checksum for {}[{}]: {}'.format(
 						w, self.seed_id, self.idxs_fmt, self.checksum))
 						w, self.seed_id, self.idxs_fmt, self.checksum))
-			out.append("# Record this value to a secure location.\n")
-		out.append("%s {" % self.seed_id)
+			out.append('# Record this value to a secure location.\n')
+		out.append('%s {' % self.seed_id)
 
 
 		# Body
 		# Body
-		fs = "  {:<%s}  {:<34}{}" % len(str(self.addrdata[-1].idx))
+		fs = '  {:<%s}  {:<34}{}' % len(str(self.addrdata[-1].idx))
 		for e in self.addrdata:
 		for e in self.addrdata:
-			c = ""
+			c = ''
 			if enable_comments:
 			if enable_comments:
-				try:    c = " "+e.comment
+				try:    c = ' '+e.comment
 				except: pass
 				except: pass
 			if status[0]:  # First line with idx
 			if status[0]:  # First line with idx
 				out.append(fs.format(e.idx, e.addr,c))
 				out.append(fs.format(e.idx, e.addr,c))
 			else:
 			else:
-				out.append(fs.format(e.idx, "wif: "+e.wif,c))
+				out.append(fs.format(e.idx, 'wif: '+e.wif,c))
 
 
 			if status[1]:   # Subsequent lines
 			if status[1]:   # Subsequent lines
 				if status[2]:
 				if status[2]:
-					out.append(fs.format("", "hex: "+e.sec,c))
+					out.append(fs.format('', 'hex: '+e.sec,c))
 				if status[0]:
 				if status[0]:
-					out.append(fs.format("", "wif: "+e.wif,c))
+					out.append(fs.format('', 'wif: '+e.wif,c))
 
 
-		out.append("}")
+		out.append('}')
 
 
-		return "\n".join([l.rstrip() for l in out]) + "\n"
+		return '\n'.join([l.rstrip() for l in out]) + '\n'
 
 
 
 
 	def fmt_addr_idxs(self):
 	def fmt_addr_idxs(self):
 
 
 		try: int(self.addrdata[0].idx)
 		try: int(self.addrdata[0].idx)
 		except:
 		except:
-			self.idxs_fmt = "(no idxs)"
+			self.idxs_fmt = '(no idxs)'
 			return
 			return
 
 
 		addr_idxs = [e.idx for e in self.addrdata]
 		addr_idxs = [e.idx for e in self.addrdata]
@@ -432,10 +426,10 @@ class AddrInfo(MMGenObject):
 
 
 		for i in addr_idxs[1:]:
 		for i in addr_idxs[1:]:
 			if i == prev + 1:
 			if i == prev + 1:
-				if i == addr_idxs[-1]: ret += "-", i
+				if i == addr_idxs[-1]: ret += '-', i
 			else:
 			else:
-				if prev != ret[-1]: ret += "-", prev
-				ret += ",", i
+				if prev != ret[-1]: ret += '-', prev
+				ret += ',', i
 			prev = i
 			prev = i
 
 
-		self.idxs_fmt = "".join([str(i) for i in ret])
+		self.idxs_fmt = ''.join([str(i) for i in ret])

+ 21 - 21
mmgen/bitcoin.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # MMGen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # MMGen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@ _Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
 curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
 curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
 generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
 generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
 oid_secp256k1 = (1,3,132,0,10)
 oid_secp256k1 = (1,3,132,0,10)
-secp256k1 = ecdsa.curves.Curve("secp256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1)
+secp256k1 = ecdsa.curves.Curve('secp256k1', curve_secp256k1, generator_secp256k1, oid_secp256k1)
 
 
 b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
 b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
 
 
@@ -49,8 +49,8 @@ b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
 #
 #
 # Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
 # Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
 #
 #
-# The "zero address":
-# 1111111111111111111114oLvT2 (use step2 = ("0" * 40) to generate)
+# The 'zero address':
+# 1111111111111111111114oLvT2 (use step2 = ('0' * 40) to generate)
 #
 #
 
 
 def pubhex2hexaddr(pubhex):
 def pubhex2hexaddr(pubhex):
@@ -63,8 +63,8 @@ def hexaddr2addr(hexaddr, vers_num='00'):
 	step1 = sha256(unhexlify(hexaddr2)).digest()
 	step1 = sha256(unhexlify(hexaddr2)).digest()
 	step2 = sha256(step1).hexdigest()
 	step2 = sha256(step1).hexdigest()
 	pubkey = hexaddr2 + step2[:8]
 	pubkey = hexaddr2 + step2[:8]
-	lzeroes = (len(hexaddr2) - len(hexaddr2.lstrip("0"))) / 2
-	return ("1" * lzeroes) + _numtob58(int(pubkey,16))
+	lzeroes = (len(hexaddr2) - len(hexaddr2.lstrip('0'))) / 2
+	return ('1' * lzeroes) + _numtob58(int(pubkey,16))
 
 
 def verify_addr(addr,verbose=False,return_hex=False):
 def verify_addr(addr,verbose=False,return_hex=False):
 
 
@@ -72,7 +72,7 @@ def verify_addr(addr,verbose=False,return_hex=False):
 		if addr[0] != ldigit: continue
 		if addr[0] != ldigit: continue
 		num = _b58tonum(addr)
 		num = _b58tonum(addr)
 		if num == False: break
 		if num == False: break
-		addr_hex = "{:050x}".format(num)
+		addr_hex = '{:050x}'.format(num)
 		if addr_hex[:2] != vers_num: continue
 		if addr_hex[:2] != vers_num: continue
 		step1 = sha256(unhexlify(addr_hex[:42])).digest()
 		step1 = sha256(unhexlify(addr_hex[:42])).digest()
 		step2 = sha256(step1).hexdigest()
 		step2 = sha256(step1).hexdigest()
@@ -101,7 +101,7 @@ def _b58tonum(b58num):
 	return sum([b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1]))])
 	return sum([b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1]))])
 
 
 def numtowif(numpriv):
 def numtowif(numpriv):
-	step1 = '80' + "{:064x}".format(numpriv)
+	step1 = '80' + '{:064x}'.format(numpriv)
 	step2 = sha256(unhexlify(step1)).digest()
 	step2 = sha256(unhexlify(step1)).digest()
 	step3 = sha256(step2).hexdigest()
 	step3 = sha256(step2).hexdigest()
 	key = step1 + step3[:8]
 	key = step1 + step3[:8]
@@ -113,17 +113,17 @@ def numtowif(numpriv):
 # (well, not exactly: they yield numeric but not bytewise equivalence)
 # (well, not exactly: they yield numeric but not bytewise equivalence)
 
 
 def b58encode(s):
 def b58encode(s):
-	if s == "": return ""
+	if s == '': return ''
 	num = int(hexlify(s),16)
 	num = int(hexlify(s),16)
 	return _numtob58(num)
 	return _numtob58(num)
 
 
 def b58decode(b58num):
 def b58decode(b58num):
-	if b58num == "": return ""
+	if b58num == '': return ''
 	# Zap all spaces:
 	# Zap all spaces:
 	num = _b58tonum(b58num.translate(None,' \t\n\r'))
 	num = _b58tonum(b58num.translate(None,' \t\n\r'))
 	if num == False: return False
 	if num == False: return False
-	out = "{:x}".format(num)
-	return unhexlify("0"*(len(out)%2) + out)
+	out = '{:x}'.format(num)
+	return unhexlify('0'*(len(out)%2) + out)
 
 
 # These yield bytewise equivalence in our special cases:
 # These yield bytewise equivalence in our special cases:
 
 
@@ -134,36 +134,36 @@ def _b58_pad(s,a,b,pad,f,w):
 	try:
 	try:
 		outlen = b[a.index(len(s))]
 		outlen = b[a.index(len(s))]
 	except:
 	except:
-		Msg("_b58_pad() accepts only %s %s bytes long "\
-			"(input was %s bytes)" % (w,",".join([str(i) for i in a]),len(s)))
+		Msg('_b58_pad() accepts only %s %s bytes long '\
+			'(input was %s bytes)' % (w,','.join([str(i) for i in a]),len(s)))
 		return False
 		return False
 
 
 	out = f(s)
 	out = f(s)
 	if out == False: return False
 	if out == False: return False
-	return "%s%s" % (pad * (outlen - len(out)), out)
+	return '%s%s' % (pad * (outlen - len(out)), out)
 
 
 def b58encode_pad(s):
 def b58encode_pad(s):
 	return _b58_pad(s,
 	return _b58_pad(s,
-		a=bin_lens,b=b58_lens,pad="1",f=b58encode,w="binary strings")
+		a=bin_lens,b=b58_lens,pad='1',f=b58encode,w='binary strings')
 
 
 def b58decode_pad(s):
 def b58decode_pad(s):
 	return _b58_pad(s,
 	return _b58_pad(s,
-		a=b58_lens,b=bin_lens,pad='\0',f=b58decode,w="base 58 numbers")
+		a=b58_lens,b=bin_lens,pad='\0',f=b58decode,w='base 58 numbers')
 
 
 # Compressed address support:
 # Compressed address support:
 
 
 def wiftohex(wifpriv,compressed=False):
 def wiftohex(wifpriv,compressed=False):
-	idx = 68 if compressed else 66
+	idx = (66,68)[bool(compressed)]
 	num = _b58tonum(wifpriv)
 	num = _b58tonum(wifpriv)
 	if num == False: return False
 	if num == False: return False
-	key = "{:x}".format(num)
+	key = '{:x}'.format(num)
 	if compressed and key[66:68] != '01': return False
 	if compressed and key[66:68] != '01': return False
 	round1 = sha256(unhexlify(key[:idx])).digest()
 	round1 = sha256(unhexlify(key[:idx])).digest()
 	round2 = sha256(round1).hexdigest()
 	round2 = sha256(round1).hexdigest()
 	return key[2:66] if (key[:2] == '80' and key[idx:] == round2[:8]) else False
 	return key[2:66] if (key[:2] == '80' and key[idx:] == round2[:8]) else False
 
 
 def hextowif(hexpriv,compressed=False):
 def hextowif(hexpriv,compressed=False):
-	step1 = '80' + hexpriv + ('01' if compressed else '')
+	step1 = '80' + hexpriv + ('','01')[bool(compressed)]
 	step2 = sha256(unhexlify(step1)).digest()
 	step2 = sha256(unhexlify(step1)).digest()
 	step3 = sha256(step2).hexdigest()
 	step3 = sha256(step2).hexdigest()
 	key = step1 + step3[:8]
 	key = step1 + step3[:8]
@@ -173,7 +173,7 @@ def privnum2pubhex(numpriv,compressed=False):
 	pko = ecdsa.SigningKey.from_secret_exponent(numpriv,secp256k1)
 	pko = ecdsa.SigningKey.from_secret_exponent(numpriv,secp256k1)
 	pubkey = hexlify(pko.get_verifying_key().to_string())
 	pubkey = hexlify(pko.get_verifying_key().to_string())
 	if compressed:
 	if compressed:
-		p = '02' if pubkey[-1] in "02468ace" else '03'
+		p = ('03','02')[pubkey[-1] in '02468ace']
 		return p+pubkey[:64]
 		return p+pubkey[:64]
 	else:
 	else:
 		return '04'+pubkey
 		return '04'+pubkey

+ 8 - 3
mmgen/opt.py → mmgen/common.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -17,6 +17,11 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
 """
 """
-opt.py: a namespace for the global opt variables
+common.py:  Common imports for all MMGen scripts
 """
 """
-import opts
+
+import sys
+import mmgen.globalvars as g
+import mmgen.opts as opts
+from mmgen.opts import opt
+from mmgen.util import *

+ 59 - 63
mmgen/crypto.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,13 +20,10 @@
 crypto.py:  Cryptographic and related routines for the MMGen suite
 crypto.py:  Cryptographic and related routines for the MMGen suite
 """
 """
 
 
-import sys
 from binascii import hexlify
 from binascii import hexlify
 from hashlib import sha256
 from hashlib import sha256
 
 
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import *
+from mmgen.common import *
 from mmgen.term import get_char
 from mmgen.term import get_char
 
 
 crmsg = {
 crmsg = {
@@ -58,73 +55,72 @@ keystrokes will also be used as a source of randomness.
 }
 }
 
 
 def encrypt_seed(seed, key):
 def encrypt_seed(seed, key):
-	return encrypt_data(seed, key, iv=1, desc="seed")
+	return encrypt_data(seed, key, iv=1, desc='seed')
 
 
 
 
 def decrypt_seed(enc_seed, key, seed_id, key_id):
 def decrypt_seed(enc_seed, key, seed_id, key_id):
 
 
-	vmsg_r("Checking key...")
+	vmsg_r('Checking key...')
 	chk1 = make_chksum_8(key)
 	chk1 = make_chksum_8(key)
 	if key_id:
 	if key_id:
-		if not compare_chksums(key_id,"key ID",chk1,"computed"):
-			msg("Incorrect passphrase or hash preset")
+		if not compare_chksums(key_id,'key ID',chk1,'computed'):
+			msg('Incorrect passphrase or hash preset')
 			return False
 			return False
 
 
-	dec_seed = decrypt_data(enc_seed, key, iv=1, desc="seed")
+	dec_seed = decrypt_data(enc_seed, key, iv=1, desc='seed')
 
 
 	chk2 = make_chksum_8(dec_seed)
 	chk2 = make_chksum_8(dec_seed)
 
 
 	if seed_id:
 	if seed_id:
-		if compare_chksums(seed_id,"Seed ID",chk2,"decrypted seed"):
-			qmsg("Passphrase is OK")
+		if compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
+			qmsg('Passphrase is OK')
 		else:
 		else:
 			if not opt.debug:
 			if not opt.debug:
-				msg_r("Checking key ID...")
-				if compare_chksums(key_id,"key ID",chk1,"computed"):
-					msg("Key ID is correct but decryption of seed failed")
+				msg_r('Checking key ID...')
+				if compare_chksums(key_id,'key ID',chk1,'computed'):
+					msg('Key ID is correct but decryption of seed failed')
 				else:
 				else:
-					msg("Incorrect passphrase or hash preset")
+					msg('Incorrect passphrase or hash preset')
 
 
-			vmsg("")
+			vmsg('')
 			return False
 			return False
 #	else:
 #	else:
-#		qmsg("Generated IDs (Seed/Key): %s/%s" % (chk2,chk1))
+#		qmsg('Generated IDs (Seed/Key): %s/%s' % (chk2,chk1))
 
 
-	dmsg("Decrypted seed: %s" % hexlify(dec_seed))
+	dmsg('Decrypted seed: %s' % hexlify(dec_seed))
 
 
 	return dec_seed
 	return dec_seed
 
 
 
 
-def encrypt_data(data, key, iv=1, desc="data", verify=True):
+def encrypt_data(data, key, iv=1, desc='data', verify=True):
 
 
 	# 192-bit seed is 24 bytes -> not multiple of 16.  Must use MODE_CTR
 	# 192-bit seed is 24 bytes -> not multiple of 16.  Must use MODE_CTR
 	from Crypto.Cipher import AES
 	from Crypto.Cipher import AES
 	from Crypto.Util import Counter
 	from Crypto.Util import Counter
 
 
-	vmsg("Encrypting %s" % desc)
+	vmsg('Encrypting %s' % desc)
 
 
 	c = AES.new(key, AES.MODE_CTR,
 	c = AES.new(key, AES.MODE_CTR,
 			counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 			counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 	enc_data = c.encrypt(data)
 	enc_data = c.encrypt(data)
 
 
 	if verify:
 	if verify:
-		vmsg_r("Performing a test decryption of the %s..." % desc)
+		vmsg_r('Performing a test decryption of the %s...' % desc)
 
 
 		c = AES.new(key, AES.MODE_CTR,
 		c = AES.new(key, AES.MODE_CTR,
 				counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 				counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 		dec_data = c.decrypt(enc_data)
 		dec_data = c.decrypt(enc_data)
 
 
-		if dec_data == data: vmsg("done")
+		if dec_data == data: vmsg('done')
 		else:
 		else:
-			msg("ERROR.\nDecrypted %s doesn't match original %s" % (desc,desc))
-			sys.exit(2)
+			die(2,"ERROR.\nDecrypted %s doesn't match original %s" % (desc,desc))
 
 
 	return enc_data
 	return enc_data
 
 
 
 
-def decrypt_data(enc_data, key, iv=1, desc="data"):
+def decrypt_data(enc_data, key, iv=1, desc='data'):
 
 
-	vmsg_r("Decrypting %s with key..." % desc)
+	vmsg_r('Decrypting %s with key...' % desc)
 
 
 	from Crypto.Cipher import AES
 	from Crypto.Cipher import AES
 	from Crypto.Util import Counter
 	from Crypto.Util import Counter
@@ -147,66 +143,66 @@ def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
 
 
 
 
 def make_key(passwd,salt,hash_preset,
 def make_key(passwd,salt,hash_preset,
-		desc="encryption key",from_what="passphrase",verbose=False):
+		desc='encryption key',from_what='passphrase',verbose=False):
 
 
-	if from_what: desc += " from "
+	if from_what: desc += ' from '
 	if opt.verbose or verbose:
 	if opt.verbose or verbose:
-		msg_r("Generating %s%s..." % (desc,from_what))
+		msg_r('Generating %s%s...' % (desc,from_what))
 	key = scrypt_hash_passphrase(passwd, salt, hash_preset)
 	key = scrypt_hash_passphrase(passwd, salt, hash_preset)
-	if opt.verbose or verbose: msg("done")
-	dmsg("Key: %s" % hexlify(key))
+	if opt.verbose or verbose: msg('done')
+	dmsg('Key: %s' % hexlify(key))
 	return key
 	return key
 
 
 
 
 def _get_random_data_from_user(uchars):
 def _get_random_data_from_user(uchars):
 
 
-	if opt.quiet: msg("Enter %s random symbols" % uchars)
+	if opt.quiet: msg('Enter %s random symbols' % uchars)
 	else:       msg(crmsg['usr_rand_notice'] % uchars)
 	else:       msg(crmsg['usr_rand_notice'] % uchars)
 
 
-	prompt = "You may begin typing.  %s symbols left: "
+	prompt = 'You may begin typing.  %s symbols left: '
 	msg_r(prompt % uchars)
 	msg_r(prompt % uchars)
 
 
 	import time
 	import time
 	# time.clock() always returns zero, so we'll use time.time()
 	# time.clock() always returns zero, so we'll use time.time()
 	saved_time = time.time()
 	saved_time = time.time()
 
 
-	key_data,time_data,pp = "",[],True
+	key_data,time_data,pp = '',[],True
 
 
 	for i in range(uchars):
 	for i in range(uchars):
-		key_data += get_char(immed_chars="ALL",prehold_protect=pp)
+		key_data += get_char(immed_chars='ALL',prehold_protect=pp)
 		pp = False
 		pp = False
-		msg_r("\r" + prompt % (uchars - i - 1))
+		msg_r('\r' + prompt % (uchars - i - 1))
 		now = time.time()
 		now = time.time()
 		time_data.append(now - saved_time)
 		time_data.append(now - saved_time)
 		saved_time = now
 		saved_time = now
 
 
-	if opt.quiet: msg_r("\r")
-	else: msg_r("\rThank you.  That's enough.%s\n\n" % (" "*18))
+	if opt.quiet: msg_r('\r')
+	else: msg_r("\rThank you.  That's enough.%s\n\n" % (' '*18))
 
 
-	fmt_time_data = ["{:.22f}".format(i) for i in time_data]
+	fmt_time_data = ['{:.22f}'.format(i) for i in time_data]
 
 
-	dmsg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
-				(key_data,"\n".join(fmt_time_data)))
+	dmsg('\nUser input:\n%s\nKeystroke time intervals:\n%s\n' %
+				(key_data,'\n'.join(fmt_time_data)))
 
 
-	prompt = "User random data successfully acquired.  Press ENTER to continue"
-	prompt_and_get_char(prompt,"",enter_ok=True)
+	prompt = 'User random data successfully acquired.  Press ENTER to continue'
+	prompt_and_get_char(prompt,'',enter_ok=True)
 
 
-	return key_data+"".join(fmt_time_data)
+	return key_data+''.join(fmt_time_data)
 
 
 
 
 def get_random(length):
 def get_random(length):
 	from Crypto import Random
 	from Crypto import Random
 	os_rand = Random.new().read(length)
 	os_rand = Random.new().read(length)
 	if g.use_urandchars and opt.usr_randchars:
 	if g.use_urandchars and opt.usr_randchars:
-		from_what = "OS random data"
+		from_what = 'OS random data'
 		if not g.user_entropy:
 		if not g.user_entropy:
 			g.user_entropy = \
 			g.user_entropy = \
 				sha256(_get_random_data_from_user(opt.usr_randchars)).digest()
 				sha256(_get_random_data_from_user(opt.usr_randchars)).digest()
-			from_what += " plus user-supplied entropy"
+			from_what += ' plus user-supplied entropy'
 		else:
 		else:
-			from_what += " plus saved user-supplied entropy"
-		key = make_key(g.user_entropy, "", '2', from_what=from_what, verbose=True)
-		return encrypt_data(os_rand,key,desc="random data",verify=False)
+			from_what += ' plus saved user-supplied entropy'
+		key = make_key(g.user_entropy, '', '2', from_what=from_what, verbose=True)
+		return encrypt_data(os_rand,key,desc='random data',verify=False)
 	else:
 	else:
 		return os_rand
 		return os_rand
 
 
@@ -214,13 +210,13 @@ def get_random(length):
 # Vars for mmgen_*crypt functions only
 # Vars for mmgen_*crypt functions only
 salt_len,sha256_len,nonce_len = 32,32,32
 salt_len,sha256_len,nonce_len = 32,32,32
 
 
-def mmgen_encrypt(data,desc="data",hash_preset=''):
+def mmgen_encrypt(data,desc='data',hash_preset=''):
 	salt,iv,nonce = get_random(salt_len),\
 	salt,iv,nonce = get_random(salt_len),\
 					get_random(g.aesctr_iv_len), \
 					get_random(g.aesctr_iv_len), \
 					get_random(nonce_len)
 					get_random(nonce_len)
 	hp = hash_preset or get_hash_preset_from_user('3',desc)
 	hp = hash_preset or get_hash_preset_from_user('3',desc)
-	m = "default" if hp == '3' else "user-requested"
-	vmsg("Encrypting %s" % desc)
+	m = ('user-requested','default')[hp=='3']
+	vmsg('Encrypting %s' % desc)
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
 	passwd = get_new_passphrase(desc, {})
 	passwd = get_new_passphrase(desc, {})
 	key = make_key(passwd, salt, hp)
 	key = make_key(passwd, salt, hp)
@@ -229,34 +225,34 @@ def mmgen_encrypt(data,desc="data",hash_preset=''):
 	return salt+iv+enc_d
 	return salt+iv+enc_d
 
 
 
 
-def mmgen_decrypt(data,desc="data",hash_preset=""):
+def mmgen_decrypt(data,desc='data',hash_preset=''):
 	dstart = salt_len + g.aesctr_iv_len
 	dstart = salt_len + g.aesctr_iv_len
 	salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
 	salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
-	vmsg("Preparing to decrypt %s" % desc)
+	vmsg('Preparing to decrypt %s' % desc)
 	hp = hash_preset or get_hash_preset_from_user('3',desc)
 	hp = hash_preset or get_hash_preset_from_user('3',desc)
-	m = "default" if hp == '3' else "user-requested"
+	m = ('user-requested','default')[hp=='3']
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
 	passwd = get_mmgen_passphrase(desc)
 	passwd = get_mmgen_passphrase(desc)
 	key = make_key(passwd, salt, hp)
 	key = make_key(passwd, salt, hp)
 	dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), desc)
 	dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), desc)
 	if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
 	if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
-		vmsg("OK")
+		vmsg('OK')
 		return dec_d[sha256_len+nonce_len:]
 		return dec_d[sha256_len+nonce_len:]
 	else:
 	else:
-		msg("Incorrect passphrase or hash preset")
+		msg('Incorrect passphrase or hash preset')
 		return False
 		return False
 
 
 def mmgen_decrypt_file_maybe(fn,desc):
 def mmgen_decrypt_file_maybe(fn,desc):
-	d = get_data_from_file(fn,"{} data".format(desc),binary=True)
+	d = get_data_from_file(fn,'{} data'.format(desc),binary=True)
 	have_enc_ext = get_extension(fn) == g.mmenc_ext
 	have_enc_ext = get_extension(fn) == g.mmenc_ext
 	if have_enc_ext or not is_ascii(d):
 	if have_enc_ext or not is_ascii(d):
-		m = ("Attempting to decrypt","Decrypting")[int(have_enc_ext)]
-		msg("%s %s %s" % (m,desc,fn))
+		m = ('Attempting to decrypt','Decrypting')[have_enc_ext]
+		msg('%s %s %s' % (m,desc,fn))
 		d = mmgen_decrypt_retry(d,desc)
 		d = mmgen_decrypt_retry(d,desc)
 	return d
 	return d
 
 
-def mmgen_decrypt_retry(d,desc="data"):
+def mmgen_decrypt_retry(d,desc='data'):
 	while True:
 	while True:
 		d_dec = mmgen_decrypt(d,desc)
 		d_dec = mmgen_decrypt(d,desc)
 		if d_dec: return d_dec
 		if d_dec: return d_dec
-		msg("Trying again...")
+		msg('Trying again...')

+ 2 - 2
mmgen/filename.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -41,7 +41,7 @@ class Filename(MMGenObject):
 				die(2,"Unrecognized extension '.%s' for file '%s'" % (self.ext,fn))
 				die(2,"Unrecognized extension '.%s' for file '%s'" % (self.ext,fn))
 
 
 		# TODO: Check for Windows
 		# TODO: Check for Windows
-		mode = (os.O_RDONLY,os.O_RDWR)[int(write)]
+		mode = (os.O_RDONLY,os.O_RDWR)[bool(write)]
 		import stat
 		import stat
 		if stat.S_ISBLK(os.stat(fn).st_mode):
 		if stat.S_ISBLK(os.stat(fn).st_mode):
 			try:
 			try:

+ 39 - 39
mmgen/globalvars.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -17,82 +17,82 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
 """
 """
-config.py:  Constants and configuration options for the MMGen suite
+globalvars.py:  Constants and configuration options for the MMGen suite
 """
 """
 
 
 import sys, os
 import sys, os
 
 
 # Variables - these might be altered at runtime:
 # Variables - these might be altered at runtime:
 
 
-user_entropy   = ""
+user_entropy   = ''
 hash_preset    = '3'
 hash_preset    = '3'
 usr_randchars  = 30
 usr_randchars  = 30
 use_urandchars = False
 use_urandchars = False
 
 
 # returns None if env var unset
 # returns None if env var unset
-debug                = os.getenv("MMGEN_DEBUG")
-no_license           = os.getenv("MMGEN_NOLICENSE")
-bogus_wallet_data    = os.getenv("MMGEN_BOGUS_WALLET_DATA")
-disable_hold_protect = os.getenv("MMGEN_DISABLE_HOLD_PROTECT")
+debug                = os.getenv('MMGEN_DEBUG')
+no_license           = os.getenv('MMGEN_NOLICENSE')
+bogus_wallet_data    = os.getenv('MMGEN_BOGUS_WALLET_DATA')
+disable_hold_protect = os.getenv('MMGEN_DISABLE_HOLD_PROTECT')
 
 
 from decimal import Decimal
 from decimal import Decimal
-tx_fee        = Decimal("0.00005")
-max_tx_fee    = Decimal("0.01")
+tx_fee        = Decimal('0.00005')
+max_tx_fee    = Decimal('0.01')
 
 
 seed_len     = 256
 seed_len     = 256
-http_timeout = 30
+http_timeout = 60
 
 
 # Constants - these don't change at runtime
 # Constants - these don't change at runtime
 
 
-proj_name = "MMGen"
+proj_name = 'MMGen'
 prog_name = os.path.basename(sys.argv[0])
 prog_name = os.path.basename(sys.argv[0])
-author    = "Philemon"
-email     = "<mmgen-py@yandex.com>"
-Cdates    = '2013-2015'
-version   = '0.8.2'
+author    = 'Philemon'
+email     = '<mmgen-py@yandex.com>'
+Cdates    = '2013-2016'
+version   = '0.8.3'
 
 
 required_opts = [
 required_opts = [
-	"quiet","verbose","debug","outdir","echo_passphrase","passwd_file",
-	"usr_randchars","stdout","show_hash_presets","label",
-	"keep_passphrase","keep_hash_preset"
+	'quiet','verbose','debug','outdir','echo_passphrase','passwd_file',
+	'usr_randchars','stdout','show_hash_presets','label',
+	'keep_passphrase','keep_hash_preset'
 ]
 ]
 incompatible_opts = (
 incompatible_opts = (
-	("quiet","verbose"),
-	("label","keep_label"),
-	("tx_id", "info"),
-	("tx_id", "terse_info"),
+	('quiet','verbose'),
+	('label','keep_label'),
+	('tx_id', 'info'),
+	('tx_id', 'terse_info'),
 )
 )
 min_screen_width = 80
 min_screen_width = 80
 
 
-wallet_ext    = "mmdat"
-seed_ext      = "mmseed"
-mn_ext        = "mmwords"
-brain_ext     = "mmbrain"
-incog_ext     = "mmincog"
-incog_hex_ext = "mmincox"
+wallet_ext    = 'mmdat'
+seed_ext      = 'mmseed'
+mn_ext        = 'mmwords'
+brain_ext     = 'mmbrain'
+incog_ext     = 'mmincog'
+incog_hex_ext = 'mmincox'
 
 
 seedfile_exts = (
 seedfile_exts = (
 	wallet_ext, seed_ext, mn_ext, brain_ext, incog_ext, incog_hex_ext
 	wallet_ext, seed_ext, mn_ext, brain_ext, incog_ext, incog_hex_ext
 )
 )
 
 
-rawtx_ext           = "raw"
-sigtx_ext           = "sig"
-addrfile_ext        = "addrs"
-addrfile_chksum_ext = "chk"
-keyfile_ext         = "keys"
-keyaddrfile_ext     = "akeys"
-mmenc_ext           = "mmenc"
+rawtx_ext           = 'raw'
+sigtx_ext           = 'sig'
+addrfile_ext        = 'addrs'
+addrfile_chksum_ext = 'chk'
+keyfile_ext         = 'keys'
+keyaddrfile_ext     = 'akeys'
+mmenc_ext           = 'mmenc'
 
 
-default_wordlist    = "electrum"
-#default_wordlist    = "tirosh"
+default_wordlist    = 'electrum'
+#default_wordlist    = 'tirosh'
 
 
 # Global value sets user opt
 # Global value sets user opt
-dfl_vars = "seed_len","hash_preset","usr_randchars","debug"
+dfl_vars = 'seed_len','hash_preset','usr_randchars','debug'
 
 
 seed_lens = 128,192,256
 seed_lens = 128,192,256
 mn_lens = [i / 32 * 3 for i in seed_lens]
 mn_lens = [i / 32 * 3 for i in seed_lens]
 
 
-keyconv_exec = "keyconv"
+keyconv_exec = 'keyconv'
 
 
 mins_per_block   = 9
 mins_per_block   = 9
 passwd_max_tries = 5
 passwd_max_tries = 5

+ 2 - 2
mmgen/license.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
 """
 """
-license.py:  Text of GPLv3
+license.py:  Copyright notice and text of GPLv3
 """
 """
 
 
 import mmgen.globalvars as g
 import mmgen.globalvars as g

+ 8 - 8
mmgen/main.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -22,15 +22,15 @@ main.py - Script launcher for the MMGen suite
 
 
 def launch(what):
 def launch(what):
 
 
-	if what in ("walletgen","walletchk","walletconv","passchg"):
-		what = "wallet"
-	if what == "keygen": what = "addrgen"
+	if what in ('walletgen','walletchk','walletconv','passchg'):
+		what = 'wallet'
+	if what == 'keygen': what = 'addrgen'
 
 
 	try: import termios
 	try: import termios
 	except: # Windows
 	except: # Windows
 		from mmgen.util import start_mscolor
 		from mmgen.util import start_mscolor
 		start_mscolor()
 		start_mscolor()
-		__import__("mmgen.main_" + what)
+		__import__('mmgen.main_' + what)
 	else:
 	else:
 		import sys,atexit
 		import sys,atexit
 		fd = sys.stdin.fileno()
 		fd = sys.stdin.fileno()
@@ -38,8 +38,8 @@ def launch(what):
 		def at_exit():
 		def at_exit():
 			termios.tcsetattr(fd, termios.TCSADRAIN, old)
 			termios.tcsetattr(fd, termios.TCSADRAIN, old)
 		atexit.register(at_exit)
 		atexit.register(at_exit)
-		try: __import__("mmgen.main_" + what)
+		try: __import__('mmgen.main_' + what)
 		except KeyboardInterrupt:
 		except KeyboardInterrupt:
-			sys.stderr.write("\nUser interrupt\n")
+			sys.stderr.write('\nUser interrupt\n')
 		except EOFError:
 		except EOFError:
-			sys.stderr.write("\nEnd of file\n")
+			sys.stderr.write('\nEnd of file\n')

+ 25 - 30
mmgen/main_addrgen.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -21,24 +21,20 @@ mmgen-addrgen: Generate a series or range of addresses from an MMGen
                deterministic wallet
                deterministic wallet
 """
 """
 
 
-import sys
-
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import *
+from mmgen.common import *
 from mmgen.crypto import *
 from mmgen.crypto import *
 from mmgen.addr import *
 from mmgen.addr import *
 from mmgen.seed import SeedSource
 from mmgen.seed import SeedSource
 
 
-if sys.argv[0].split("-")[-1] == "keygen":
-	gen_what = "keys"
+if sys.argv[0].split('-')[-1] == 'keygen':
+	gen_what = 'keys'
 	opt_filter = None
 	opt_filter = None
 	note1 = """
 	note1 = """
 By default, both addresses and secret keys are generated.
 By default, both addresses and secret keys are generated.
 """.strip()
 """.strip()
 else:
 else:
-	gen_what = "addresses"
-	opt_filter = "hcdeiHOKlpzPqSv"
+	gen_what = 'addresses'
+	opt_filter = 'hcdeiHOKlpzPqSv'
 	note1 = """
 	note1 = """
 If available, the external 'keyconv' program will be used for address
 If available, the external 'keyconv' program will be used for address
 generation.
 generation.
@@ -48,7 +44,7 @@ opts_data = {
 	'sets': [('print_checksum',True,'quiet',True)],
 	'sets': [('print_checksum',True,'quiet',True)],
 	'desc': """Generate a range or list of {what} from an {pnm} wallet,
 	'desc': """Generate a range or list of {what} from an {pnm} wallet,
                   mnemonic, seed or password""".format(what=gen_what,pnm=g.proj_name),
                   mnemonic, seed or password""".format(what=gen_what,pnm=g.proj_name),
-	'usage':"[opts] [infile] <address range or list>",
+	'usage':'[opts] [infile] <address range or list>',
 	'options': """
 	'options': """
 -h, --help            Print this help message.
 -h, --help            Print this help message.
 -A, --no-addresses    Print only secret keys, no addresses.
 -A, --no-addresses    Print only secret keys, no addresses.
@@ -73,7 +69,7 @@ opts_data = {
 -v, --verbose         Produce more verbose output.
 -v, --verbose         Produce more verbose output.
 -x, --b16             Print secret keys in hexadecimal too.
 -x, --b16             Print secret keys in hexadecimal too.
 """.format(
 """.format(
-	seed_lens=", ".join([str(i) for i in g.seed_lens]),
+	seed_lens=', '.join([str(i) for i in g.seed_lens]),
 	pnm=g.proj_name,
 	pnm=g.proj_name,
 	what=gen_what,g=g
 	what=gen_what,g=g
 ),
 ),
@@ -92,21 +88,20 @@ FMT CODES:
   {f}
   {f}
 """.format(
 """.format(
 		n=note1,
 		n=note1,
-		f="\n  ".join(SeedSource.format_fmt_codes().splitlines()),
-		o=opt.opts
+		f='\n  '.join(SeedSource.format_fmt_codes().splitlines()),
+		o=opts
 	)
 	)
 }
 }
 
 
-
-cmd_args = opt.opts.init(opts_data,add_opts=["b16"],opt_filter=opt_filter)
+cmd_args = opts.init(opts_data,add_opts=['b16'],opt_filter=opt_filter)
 
 
 nargs = 2
 nargs = 2
 if len(cmd_args) < nargs \
 if len(cmd_args) < nargs \
 		and not opt.hidden_incog_input_params and not opt.in_fmt:
 		and not opt.hidden_incog_input_params and not opt.in_fmt:
-	opt.opts.usage()
+	opts.usage()
 elif len(cmd_args) > nargs \
 elif len(cmd_args) > nargs \
 		or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
 		or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
-			opt.opts.usage()
+			opts.usage()
 
 
 addrlist_arg = cmd_args.pop()
 addrlist_arg = cmd_args.pop()
 addr_idxs = parse_addr_idxs(addrlist_arg)
 addr_idxs = parse_addr_idxs(addrlist_arg)
@@ -115,8 +110,8 @@ if not addr_idxs:
 
 
 do_license_msg()
 do_license_msg()
 
 
-opt.gen_what = "a" if gen_what == "addresses" \
-					else "k" if opt.no_addresses else "ka"
+opt.gen_what = 'a' if gen_what == 'addresses' \
+					else 'k' if opt.no_addresses else 'ka'
 
 
 # Generate data:
 # Generate data:
 ss = SeedSource(*cmd_args)
 ss = SeedSource(*cmd_args)
@@ -124,19 +119,19 @@ ss = SeedSource(*cmd_args)
 ainfo = generate_addrs(ss.seed.data,addr_idxs)
 ainfo = generate_addrs(ss.seed.data,addr_idxs)
 
 
 addrdata_str = ainfo.fmt_data()
 addrdata_str = ainfo.fmt_data()
-outfile_base = "{}[{}]".format(ss.seed.sid, ainfo.idxs_fmt)
+outfile_base = '{}[{}]'.format(ss.seed.sid, ainfo.idxs_fmt)
 
 
 if 'a' in opt.gen_what and opt.print_checksum:
 if 'a' in opt.gen_what and opt.print_checksum:
 	Die(0,ainfo.checksum)
 	Die(0,ainfo.checksum)
 
 
-if 'k' in opt.gen_what and keypress_confirm("Encrypt key list?"):
-	addrdata_str = mmgen_encrypt(addrdata_str,"new key list","")
-	enc_ext = "." + g.mmenc_ext
-else: enc_ext = ""
+if 'k' in opt.gen_what and keypress_confirm('Encrypt key list?'):
+	addrdata_str = mmgen_encrypt(addrdata_str,'new key list','')
+	enc_ext = '.' + g.mmenc_ext
+else: enc_ext = ''
 
 
-ext = (g.keyfile_ext,g.keyaddrfile_ext)[int("ka" in opt.gen_what)]
-ext = (g.addrfile_ext,ext)[int("k" in opt.gen_what)]
-outfile = "%s.%s%s" % (outfile_base, ext, enc_ext)
-ask_tty = "k" in opt.gen_what and not opt.quiet
-if gen_what == "keys": gen_what = "secret keys"
+ext = (g.keyfile_ext,g.keyaddrfile_ext)['ka' in opt.gen_what]
+ext = (g.addrfile_ext,ext)['k' in opt.gen_what]
+outfile = '%s.%s%s' % (outfile_base, ext, enc_ext)
+ask_tty = 'k' in opt.gen_what and not opt.quiet
+if gen_what == 'keys': gen_what = 'secret keys'
 write_data_to_file(outfile,addrdata_str,gen_what,ask_tty=ask_tty)
 write_data_to_file(outfile,addrdata_str,gen_what,ask_tty=ask_tty)

+ 43 - 38
mmgen/main_addrimport.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,19 +20,19 @@
 mmgen-addrimport: Import addresses into a MMGen bitcoind tracking wallet
 mmgen-addrimport: Import addresses into a MMGen bitcoind tracking wallet
 """
 """
 
 
-import sys, time
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import *
+import time
+
+from mmgen.common import *
 from mmgen.tx import connect_to_bitcoind
 from mmgen.tx import connect_to_bitcoind
 from mmgen.addr import AddrInfo,AddrInfoEntry
 from mmgen.addr import AddrInfo,AddrInfoEntry
 
 
 opts_data = {
 opts_data = {
 	'desc': """Import addresses (both {pnm} and non-{pnm}) into a bitcoind
 	'desc': """Import addresses (both {pnm} and non-{pnm}) into a bitcoind
                      tracking wallet""".format(pnm=g.proj_name),
                      tracking wallet""".format(pnm=g.proj_name),
-	'usage':"[opts] [mmgen address file]",
+	'usage':'[opts] [mmgen address file]',
 	'options': """
 	'options': """
 -h, --help         Print this help message
 -h, --help         Print this help message
+-b, --batch        Batch mode.  Import all addresses in one RPC call
 -l, --addrlist     Address source is a flat list of addresses
 -l, --addrlist     Address source is a flat list of addresses
 -k, --keyaddr-file Address source is a key-address file
 -k, --keyaddr-file Address source is a key-address file
 -q, --quiet        Suppress warnings
 -q, --quiet        Suppress warnings
@@ -46,14 +46,14 @@ in the tracking wallet.
 """
 """
 }
 }
 
 
-cmd_args = opt.opts.init(opts_data)
+cmd_args = opts.init(opts_data)
 
 
 if len(cmd_args) == 1:
 if len(cmd_args) == 1:
 	infile = cmd_args[0]
 	infile = cmd_args[0]
 	check_infile(infile)
 	check_infile(infile)
 	if opt.addrlist:
 	if opt.addrlist:
 		lines = get_lines_from_file(
 		lines = get_lines_from_file(
-			infile,"non-{pnm} addresses".format(pnm=g.proj_name),trim_comments=True)
+			infile,'non-{pnm} addresses'.format(pnm=g.proj_name),trim_comments=True)
 		ai,adata = AddrInfo(),[]
 		ai,adata = AddrInfo(),[]
 		for btcaddr in lines:
 		for btcaddr in lines:
 			a = AddrInfoEntry()
 			a = AddrInfoEntry()
@@ -63,23 +63,19 @@ if len(cmd_args) == 1:
 	else:
 	else:
 		ai = AddrInfo(infile,has_keys=opt.keyaddr_file)
 		ai = AddrInfo(infile,has_keys=opt.keyaddr_file)
 else:
 else:
-	msg("""
+	die(1,"""
 You must specify an {pnm} address file (or a list of non-{pnm} addresses
 You must specify an {pnm} address file (or a list of non-{pnm} addresses
 with the '--addrlist' option)
 with the '--addrlist' option)
 """.strip().format(pnm=g.proj_name))
 """.strip().format(pnm=g.proj_name))
-	sys.exit(1)
 
 
 from mmgen.bitcoin import verify_addr
 from mmgen.bitcoin import verify_addr
-qmsg_r("Validating addresses...")
+qmsg_r('Validating addresses...')
 for e in ai.addrdata:
 for e in ai.addrdata:
 	if not verify_addr(e.addr,verbose=True):
 	if not verify_addr(e.addr,verbose=True):
-		msg("%s: invalid address" % e.addr)
-		sys.exit(2)
-
-m = (" from Seed ID %s" % ai.seed_id) if ai.seed_id else ""
-qmsg("OK. %s addresses%s" % (ai.num_addrs,m))
+		die(2,'%s: invalid address' % e.addr)
 
 
-g.http_timeout = 3600
+m = (' from Seed ID %s' % ai.seed_id) if ai.seed_id else ''
+qmsg('OK. %s addresses%s' % (ai.num_addrs,m))
 
 
 if not opt.test:
 if not opt.test:
 	c = connect_to_bitcoind()
 	c = connect_to_bitcoind()
@@ -97,35 +93,40 @@ program now and rerun it using the '--rescan' option.  Otherwise you may ignore
 this message and continue.
 this message and continue.
 """.strip()
 """.strip()
 
 
-if not opt.quiet: confirm_or_exit(m, "continue", expect="YES")
+if not opt.quiet: confirm_or_exit(m, 'continue', expect='YES')
 
 
 err_flag = False
 err_flag = False
 
 
 def import_address(addr,label,rescan):
 def import_address(addr,label,rescan):
 	try:
 	try:
 		if not opt.test:
 		if not opt.test:
-			c.importaddress(addr,label,rescan)
+			c.importaddress(addr,label,rescan,timeout=(False,3600)[rescan])
 	except:
 	except:
 		global err_flag
 		global err_flag
 		err_flag = True
 		err_flag = True
 
 
 w_n_of_m = len(str(ai.num_addrs)) * 2 + 2
 w_n_of_m = len(str(ai.num_addrs)) * 2 + 2
-w_mmid   = "" if opt.addrlist else len(str(max(ai.idxs()))) + 12
+w_mmid   = '' if opt.addrlist else len(str(max(ai.idxs()))) + 12
 
 
 if opt.rescan:
 if opt.rescan:
 	import threading
 	import threading
-	msg_fmt = "\r%s %-{}s %-34s %s".format(w_n_of_m)
+	msg_fmt = '\r%s %-{}s %-34s %s'.format(w_n_of_m)
 else:
 else:
-	msg_fmt = "\r%-{}s %-34s %s".format(w_n_of_m, w_mmid)
+	msg_fmt = '\r%-{}s %-34s %s'.format(w_n_of_m, w_mmid)
+
+msg("Importing %s addresses from '%s'%s" %
+		(len(ai.addrdata),infile,('',' (batch mode)')[bool(opt.batch)]))
 
 
-msg("Importing addresses")
+arg_list = []
 for n,e in enumerate(ai.addrdata):
 for n,e in enumerate(ai.addrdata):
 	if e.idx:
 	if e.idx:
-		label = "%s:%s" % (ai.seed_id,e.idx)
-		if e.comment: label += " " + e.comment
-	else: label = "non-{pnm}".format(pnm=g.proj_name)
+		label = '%s:%s' % (ai.seed_id,e.idx)
+		if e.comment: label += ' ' + e.comment
+	else: label = 'non-{pnm}'.format(pnm=g.proj_name)
 
 
-	if opt.rescan:
+	if opt.batch:
+		arg_list.append((e.addr,label,False))
+	elif opt.rescan:
 		t = threading.Thread(target=import_address, args=(e.addr,label,True))
 		t = threading.Thread(target=import_address, args=(e.addr,label,True))
 		t.daemon = True
 		t.daemon = True
 		t.start()
 		t.start()
@@ -135,18 +136,22 @@ for n,e in enumerate(ai.addrdata):
 		while True:
 		while True:
 			if t.is_alive():
 			if t.is_alive():
 				elapsed = int(time.time() - start)
 				elapsed = int(time.time() - start)
-				count = "%s/%s:" % (n+1, ai.num_addrs)
-				msg_r(msg_fmt % (secs_to_hms(elapsed),count,e.addr,"(%s)"%label))
+				count = '%s/%s:' % (n+1, ai.num_addrs)
+				msg_r(msg_fmt % (secs_to_hms(elapsed),count,e.addr,'(%s)'%label))
 				time.sleep(1)
 				time.sleep(1)
 			else:
 			else:
-				if err_flag: msg("\nImport failed"); sys.exit(2)
-				msg("\nOK")
+				if err_flag: die(2,'\nImport failed')
+				msg('\nOK')
 				break
 				break
 	else:
 	else:
-		import_address(e.addr,label,rescan=False)
-		count = "%s/%s:" % (n+1, ai.num_addrs)
-		msg_r(msg_fmt % (count, e.addr, "(%s)"%label))
-		if err_flag:
-			msg("\nImport failed")
-			sys.exit(2)
-		msg(" - OK")
+		import_address(e.addr,label,False)
+		count = '%s/%s:' % (n+1, ai.num_addrs)
+		msg_r(msg_fmt % (count, e.addr, '(%s)'%label))
+		if err_flag: die(2,'\nImport failed')
+		msg(' - OK')
+
+if opt.batch:
+	if opt.rescan:
+		msg('Warning: this command may take a long time to complete!')
+	ret = c.importaddress(arg_list,batch=True,timeout=(False,3600)[bool(opt.rescan)])
+	msg('OK: %s addresses imported' % len(ret))

+ 0 - 1684
mmgen/main_pywallet.py

@@ -1,1684 +0,0 @@
-#!/usr/bin/env python
-#
-# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-mmgen-pywallet: Dump contents of a bitcoind wallet to file
-"""
-
-# Changes by Philemon:
-#   password entry at prompt
-#   dump keys, addresses or keys for specified addresses (output in flat list)
-
-# PyWallet 1.2.1 (Public Domain)
-# http://github.com/joric/pywallet
-# Most of the actual PyWallet code placed in the public domain.
-# PyWallet includes portions of free software, listed below.
-
-# BitcoinTools (wallet.dat handling code, MIT License)
-# https://github.com/gavinandresen/bitcointools
-# Copyright (c) 2010 Gavin Andresen
-
-# python-ecdsa (EC_KEY implementation, MIT License)
-# http://github.com/warner/python-ecdsa
-# "python-ecdsa" Copyright (c) 2010 Brian Warner
-# Portions written in 2005 by Peter Pearson and placed in the public domain.
-
-# SlowAES (aes.py code, Apache 2 License)
-# http://code.google.com/p/slowaes/
-# Copyright (c) 2008, Josh Davis (http://www.josh-davis.org),
-# Alex Martelli (http://www.aleax.it)
-# Ported from C code written by Laurent Haan (http://www.progressive-coding.com)
-
-from bsddb.db import *
-import sys, time
-import json
-import logging
-import struct
-import StringIO
-import traceback
-import socket
-import types
-import string
-import exceptions
-import hashlib
-import random
-import math
-
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import msg
-
-max_version = 60000
-addrtype = 0
-json_db = {}
-private_keys = []
-password = None
-
-opts_data = {
-	'desc':    "Dump contents of a bitcoind wallet to file",
-	'usage':   "[opts] <bitcoind wallet file>",
-	'options': """
--h, --help             Print this help message.
--d, --outdir=       d  Specify an alternate directory 'd' for output.
--e, --echo-passphrase  Display passphrase on screen upon entry.
--j, --json             Dump wallet in json format.
--k, --keys             Dump all private keys (flat list).
--a, --addrs            Dump all addresses (flat list).
--K, --keysforaddrs= f  Dump private keys for addresses listed in file 'f'.
--P, --passwd-file=  f  Get passphrase from file 'f'.
--q, --quiet            Produce quieter output; suppress some warnings.
--S, --stdout           Dump to stdout rather than file.
-"""
-}
-
-cmd_args = opt.opts.init(opts_data)
-opt.opts.die_on_incompatible_opts(['json','keys','addrs','keysforaddrs'])
-
-if len(cmd_args) == 1:
-	from mmgen.util import check_infile
-	check_infile(cmd_args[0])
-else:
-	opt.opts.usage(opts_data)
-
-if (not opt.json and not opt.keys and not opt.addrs and not opt.keysforaddrs):
-			opt.opts.usage(opts_data)
-
-# from the SlowAES project, http://code.google.com/p/slowaes (aes.py)
-
-def append_PKCS7_padding(s):
-	"""return s padded to a multiple of 16-bytes by PKCS7 padding"""
-	numpads = 16 - (len(s)%16)
-	return s + numpads*chr(numpads)
-
-def strip_PKCS7_padding(s):
-	"""return s stripped of PKCS7 padding"""
-	if len(s)%16 or not s:
-		raise ValueError("String of len %d can't be PCKS7-padded" % len(s))
-	numpads = ord(s[-1])
-	if numpads > 16:
-		raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1])
-	return s[:-numpads]
-
-class AES(object):
-	# valid key sizes
-	keySize = dict(SIZE_128=16, SIZE_192=24, SIZE_256=32)
-
-	# Rijndael S-box
-	sbox =  [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67,
-			0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59,
-			0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7,
-			0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1,
-			0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05,
-			0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83,
-			0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29,
-			0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
-			0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa,
-			0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c,
-			0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc,
-			0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
-			0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
-			0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee,
-			0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
-			0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
-			0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4,
-			0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6,
-			0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70,
-			0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9,
-			0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e,
-			0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1,
-			0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0,
-			0x54, 0xbb, 0x16]
-
-	# Rijndael Inverted S-box
-	rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3,
-			0x9e, 0x81, 0xf3, 0xd7, 0xfb , 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f,
-			0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb , 0x54,
-			0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b,
-			0x42, 0xfa, 0xc3, 0x4e , 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24,
-			0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 , 0x72, 0xf8,
-			0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d,
-			0x65, 0xb6, 0x92 , 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
-			0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 , 0x90, 0xd8, 0xab,
-			0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3,
-			0x45, 0x06 , 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1,
-			0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b , 0x3a, 0x91, 0x11, 0x41,
-			0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
-			0x73 , 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9,
-			0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e , 0x47, 0xf1, 0x1a, 0x71, 0x1d,
-			0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b ,
-			0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0,
-			0xfe, 0x78, 0xcd, 0x5a, 0xf4 , 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07,
-			0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f , 0x60,
-			0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f,
-			0x93, 0xc9, 0x9c, 0xef , 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5,
-			0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 , 0x17, 0x2b,
-			0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55,
-			0x21, 0x0c, 0x7d]
-
-	def getSBoxValue(self,num):
-		"""Retrieves a given S-Box Value"""
-		return self.sbox[num]
-
-	def getSBoxInvert(self,num):
-		"""Retrieves a given Inverted S-Box Value"""
-		return self.rsbox[num]
-
-	def rotate(self, word):
-		""" Rijndael's key schedule rotate operation.
-
-        Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
-        Word is an char list of size 4 (32 bits overall).
-		"""
-		return word[1:] + word[:1]
-
-	# Rijndael Rcon
-	Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
-			0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97,
-			0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
-			0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66,
-			0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
-			0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
-			0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
-			0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61,
-			0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
-			0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
-			0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc,
-			0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
-			0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a,
-			0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,
-			0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
-			0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
-			0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4,
-			0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
-			0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08,
-			0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
-			0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
-			0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2,
-			0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74,
-			0xe8, 0xcb ]
-
-	def getRconValue(self, num):
-		"""Retrieves a given Rcon Value"""
-		return self.Rcon[num]
-
-	def core(self, word, iteration):
-		"""Key schedule core."""
-		# rotate the 32-bit word 8 bits to the left
-		word = self.rotate(word)
-		# apply S-Box substitution on all 4 parts of the 32-bit word
-		for i in range(4):
-			word[i] = self.getSBoxValue(word[i])
-		# XOR the output of the rcon operation with i to the first part
-		# (leftmost) only
-		word[0] = word[0] ^ self.getRconValue(iteration)
-		return word
-
-	def expandKey(self, key, size, expandedKeySize):
-		"""Rijndael's key expansion.
-
-        Expands an 128,192,256 key into an 176,208,240 bytes key
-
-        expandedKey is a char list of large enough size,
-        key is the non-expanded key.
-		"""
-		# current expanded keySize, in bytes
-		currentSize = 0
-		rconIteration = 1
-		expandedKey = [0] * expandedKeySize
-
-		# set the 16, 24, 32 bytes of the expanded key to the input key
-		for j in range(size):
-			expandedKey[j] = key[j]
-		currentSize += size
-
-		while currentSize < expandedKeySize:
-			# assign the previous 4 bytes to the temporary value t
-			t = expandedKey[currentSize-4:currentSize]
-
-			# every 16,24,32 bytes we apply the core schedule to t
-			# and increment rconIteration afterwards
-			if currentSize % size == 0:
-				t = self.core(t, rconIteration)
-				rconIteration += 1
-			# For 256-bit keys, we add an extra sbox to the calculation
-			if size == self.keySize["SIZE_256"] and ((currentSize % size) == 16):
-				for l in range(4): t[l] = self.getSBoxValue(t[l])
-
-			# We XOR t with the four-byte block 16,24,32 bytes before the new
-			# expanded key.  This becomes the next four bytes in the expanded
-			# key.
-			for m in range(4):
-				expandedKey[currentSize] = expandedKey[currentSize - size] ^ \
-						t[m]
-				currentSize += 1
-
-		return expandedKey
-
-	def addRoundKey(self, state, roundKey):
-		"""Adds (XORs) the round key to the state."""
-		for i in range(16):
-			state[i] ^= roundKey[i]
-		return state
-
-	def createRoundKey(self, expandedKey, roundKeyPointer):
-		"""Create a round key.
-        Creates a round key from the given expanded key and the
-        position within the expanded key.
-		"""
-		roundKey = [0] * 16
-		for i in range(4):
-			for j in range(4):
-				roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j]
-		return roundKey
-
-	def galois_multiplication(self, a, b):
-		"""Galois multiplication of 8 bit characters a and b."""
-		p = 0
-		for counter in range(8):
-			if b & 1: p ^= a
-			hi_bit_set = a & 0x80
-			a <<= 1
-			# keep a 8 bit
-			a &= 0xFF
-			if hi_bit_set:
-				a ^= 0x1b
-			b >>= 1
-		return p
-
-	#
-	# substitute all the values from the state with the value in the SBox
-	# using the state value as index for the SBox
-	#
-	def subBytes(self, state, isInv):
-		if isInv: getter = self.getSBoxInvert
-		else: getter = self.getSBoxValue
-		for i in range(16): state[i] = getter(state[i])
-		return state
-
-	# iterate over the 4 rows and call shiftRow() with that row
-	def shiftRows(self, state, isInv):
-		for i in range(4):
-			state = self.shiftRow(state, i*4, i, isInv)
-		return state
-
-	# each iteration shifts the row to the left by 1
-	def shiftRow(self, state, statePointer, nbr, isInv):
-		for i in range(nbr):
-			if isInv:
-				state[statePointer:statePointer+4] = \
-						state[statePointer+3:statePointer+4] + \
-						state[statePointer:statePointer+3]
-			else:
-				state[statePointer:statePointer+4] = \
-						state[statePointer+1:statePointer+4] + \
-						state[statePointer:statePointer+1]
-		return state
-
-	# galois multiplication of the 4x4 matrix
-	def mixColumns(self, state, isInv):
-		# iterate over the 4 columns
-		for i in range(4):
-			# construct one column by slicing over the 4 rows
-			column = state[i:i+16:4]
-			# apply the mixColumn on one column
-			column = self.mixColumn(column, isInv)
-			# put the values back into the state
-			state[i:i+16:4] = column
-
-		return state
-
-	# galois multiplication of 1 column of the 4x4 matrix
-	def mixColumn(self, column, isInv):
-		if isInv: mult = [14, 9, 13, 11]
-		else: mult = [2, 1, 1, 3]
-		cpy = list(column)
-		g = self.galois_multiplication
-
-		column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \
-					g(cpy[2], mult[2]) ^ g(cpy[1], mult[3])
-		column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \
-					g(cpy[3], mult[2]) ^ g(cpy[2], mult[3])
-		column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \
-					g(cpy[0], mult[2]) ^ g(cpy[3], mult[3])
-		column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \
-					g(cpy[1], mult[2]) ^ g(cpy[0], mult[3])
-		return column
-
-	# applies the 4 operations of the forward round in sequence
-	def aes_round(self, state, roundKey):
-		state = self.subBytes(state, False)
-		state = self.shiftRows(state, False)
-		state = self.mixColumns(state, False)
-		state = self.addRoundKey(state, roundKey)
-		return state
-
-	# applies the 4 operations of the inverse round in sequence
-	def aes_invRound(self, state, roundKey):
-		state = self.shiftRows(state, True)
-		state = self.subBytes(state, True)
-		state = self.addRoundKey(state, roundKey)
-		state = self.mixColumns(state, True)
-		return state
-
-	# Perform the initial operations, the standard round, and the final
-	# operations of the forward aes, creating a round key for each round
-	def aes_main(self, state, expandedKey, nbrRounds):
-		state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0))
-		i = 1
-		while i < nbrRounds:
-			state = self.aes_round(state, self.createRoundKey(expandedKey, 16*i))
-			i += 1
-		state = self.subBytes(state, False)
-		state = self.shiftRows(state, False)
-		state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16*nbrRounds))
-		return state
-
-	# Perform the initial operations, the standard round, and the final
-	# operations of the inverse aes, creating a round key for each round
-	def aes_invMain(self, state, expandedKey, nbrRounds):
-		state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16*nbrRounds))
-		i = nbrRounds - 1
-		while i > 0:
-			state = self.aes_invRound(state, self.createRoundKey(expandedKey, 16*i))
-			i -= 1
-		state = self.shiftRows(state, True)
-		state = self.subBytes(state, True)
-		state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0))
-		return state
-
-	# encrypts a 128 bit input block against the given key of size specified
-	def encrypt(self, iput, key, size):
-		output = [0] * 16
-		# the number of rounds
-		nbrRounds = 0
-		# the 128 bit block to encode
-		block = [0] * 16
-		# set the number of rounds
-		if size == self.keySize["SIZE_128"]: nbrRounds = 10
-		elif size == self.keySize["SIZE_192"]: nbrRounds = 12
-		elif size == self.keySize["SIZE_256"]: nbrRounds = 14
-		else: return None
-
-		# the expanded keySize
-		expandedKeySize = 16*(nbrRounds+1)
-
-		# Set the block values, for the block:
-		# a0,0 a0,1 a0,2 a0,3
-		# a1,0 a1,1 a1,2 a1,3
-		# a2,0 a2,1 a2,2 a2,3
-		# a3,0 a3,1 a3,2 a3,3
-		# the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
-		#
-		# iterate over the columns
-		for i in range(4):
-			# iterate over the rows
-			for j in range(4):
-				block[(i+(j*4))] = iput[(i*4)+j]
-
-		# expand the key into an 176, 208, 240 bytes key
-		# the expanded key
-		expandedKey = self.expandKey(key, size, expandedKeySize)
-
-		# encrypt the block using the expandedKey
-		block = self.aes_main(block, expandedKey, nbrRounds)
-
-		# unmap the block again into the output
-		for k in range(4):
-			# iterate over the rows
-			for l in range(4):
-				output[(k*4)+l] = block[(k+(l*4))]
-		return output
-
-	# decrypts a 128 bit input block against the given key of size specified
-	def decrypt(self, iput, key, size):
-		output = [0] * 16
-		# the number of rounds
-		nbrRounds = 0
-		# the 128 bit block to decode
-		block = [0] * 16
-		# set the number of rounds
-		if size == self.keySize["SIZE_128"]: nbrRounds = 10
-		elif size == self.keySize["SIZE_192"]: nbrRounds = 12
-		elif size == self.keySize["SIZE_256"]: nbrRounds = 14
-		else: return None
-
-		# the expanded keySize
-		expandedKeySize = 16*(nbrRounds+1)
-
-		# Set the block values, for the block:
-		# a0,0 a0,1 a0,2 a0,3
-		# a1,0 a1,1 a1,2 a1,3
-		# a2,0 a2,1 a2,2 a2,3
-		# a3,0 a3,1 a3,2 a3,3
-		# the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
-
-		# iterate over the columns
-		for i in range(4):
-			# iterate over the rows
-			for j in range(4):
-				block[(i+(j*4))] = iput[(i*4)+j]
-		# expand the key into an 176, 208, 240 bytes key
-		expandedKey = self.expandKey(key, size, expandedKeySize)
-		# decrypt the block using the expandedKey
-		block = self.aes_invMain(block, expandedKey, nbrRounds)
-		# unmap the block again into the output
-		for k in range(4):
-			# iterate over the rows
-			for l in range(4):
-				output[(k*4)+l] = block[(k+(l*4))]
-		return output
-
-class AESModeOfOperation(object):
-
-	aes = AES()
-
-	# structure of supported modes of operation
-	modeOfOperation = dict(OFB=0, CFB=1, CBC=2)
-
-	# converts a 16 character string into a number array
-	def convertString(self, string, start, end, mode):
-		if end - start > 16: end = start + 16
-		if mode == self.modeOfOperation["CBC"]: ar = [0] * 16
-		else: ar = []
-
-		i = start
-		j = 0
-		while len(ar) < end - start:
-			ar.append(0)
-		while i < end:
-			ar[j] = ord(string[i])
-			j += 1
-			i += 1
-		return ar
-
-	# Mode of Operation Encryption
-	# stringIn - Input String
-	# mode - mode of type modeOfOperation
-	# hexKey - a hex key of the bit length size
-	# size - the bit length of the key
-	# hexIV - the 128 bit hex Initilization Vector
-	def encrypt(self, stringIn, mode, key, size, IV):
-		if len(key) % size:
-			return None
-		if len(IV) % 16:
-			return None
-		# the AES input/output
-		plaintext = []
-		iput = [0] * 16
-		output = []
-		ciphertext = [0] * 16
-		# the output cipher string
-		cipherOut = []
-		# char firstRound
-		firstRound = True
-		if stringIn != None:
-			for j in range(int(math.ceil(float(len(stringIn))/16))):
-				start = j*16
-				end = j*16+16
-				if  end > len(stringIn):
-					end = len(stringIn)
-				plaintext = self.convertString(stringIn, start, end, mode)
-				# print 'PT@%s:%s' % (j, plaintext)
-				if mode == self.modeOfOperation["CFB"]:
-					if firstRound:
-						output = self.aes.encrypt(IV, key, size)
-						firstRound = False
-					else:
-						output = self.aes.encrypt(iput, key, size)
-					for i in range(16):
-						if len(plaintext)-1 < i:
-							ciphertext[i] = 0 ^ output[i]
-						elif len(output)-1 < i:
-							ciphertext[i] = plaintext[i] ^ 0
-						elif len(plaintext)-1 < i and len(output) < i:
-							ciphertext[i] = 0 ^ 0
-						else:
-							ciphertext[i] = plaintext[i] ^ output[i]
-					for k in range(end-start):
-						cipherOut.append(ciphertext[k])
-					iput = ciphertext
-				elif mode == self.modeOfOperation["OFB"]:
-					if firstRound:
-						output = self.aes.encrypt(IV, key, size)
-						firstRound = False
-					else:
-						output = self.aes.encrypt(iput, key, size)
-					for i in range(16):
-						if len(plaintext)-1 < i:
-							ciphertext[i] = 0 ^ output[i]
-						elif len(output)-1 < i:
-							ciphertext[i] = plaintext[i] ^ 0
-						elif len(plaintext)-1 < i and len(output) < i:
-							ciphertext[i] = 0 ^ 0
-						else:
-							ciphertext[i] = plaintext[i] ^ output[i]
-					for k in range(end-start):
-						cipherOut.append(ciphertext[k])
-					iput = output
-				elif mode == self.modeOfOperation["CBC"]:
-					for i in range(16):
-						if firstRound:
-							iput[i] =  plaintext[i] ^ IV[i]
-						else:
-							iput[i] =  plaintext[i] ^ ciphertext[i]
-					# print 'IP@%s:%s' % (j, iput)
-					firstRound = False
-					ciphertext = self.aes.encrypt(iput, key, size)
-					# always 16 bytes because of the padding for CBC
-					for k in range(16):
-						cipherOut.append(ciphertext[k])
-		return mode, len(stringIn), cipherOut
-
-	# Mode of Operation Decryption
-	# cipherIn - Encrypted String
-	# originalsize - The unencrypted string length - required for CBC
-	# mode - mode of type modeOfOperation
-	# key - a number array of the bit length size
-	# size - the bit length of the key
-	# IV - the 128 bit number array Initilization Vector
-	def decrypt(self, cipherIn, originalsize, mode, key, size, IV):
-		# cipherIn = unescCtrlChars(cipherIn)
-		if len(key) % size:
-			return None
-		if len(IV) % 16:
-			return None
-		# the AES input/output
-		ciphertext = []
-		iput = []
-		output = []
-		plaintext = [0] * 16
-		# the output plain text string
-		stringOut = ''
-		# char firstRound
-		firstRound = True
-		if cipherIn != None:
-			for j in range(int(math.ceil(float(len(cipherIn))/16))):
-				start = j*16
-				end = j*16+16
-				if j*16+16 > len(cipherIn):
-					end = len(cipherIn)
-				ciphertext = cipherIn[start:end]
-				if mode == self.modeOfOperation["CFB"]:
-					if firstRound:
-						output = self.aes.encrypt(IV, key, size)
-						firstRound = False
-					else:
-						output = self.aes.encrypt(iput, key, size)
-					for i in range(16):
-						if len(output)-1 < i:
-							plaintext[i] = 0 ^ ciphertext[i]
-						elif len(ciphertext)-1 < i:
-							plaintext[i] = output[i] ^ 0
-						elif len(output)-1 < i and len(ciphertext) < i:
-							plaintext[i] = 0 ^ 0
-						else:
-							plaintext[i] = output[i] ^ ciphertext[i]
-					for k in range(end-start):
-						stringOut += chr(plaintext[k])
-					iput = ciphertext
-				elif mode == self.modeOfOperation["OFB"]:
-					if firstRound:
-						output = self.aes.encrypt(IV, key, size)
-						firstRound = False
-					else:
-						output = self.aes.encrypt(iput, key, size)
-					for i in range(16):
-						if len(output)-1 < i:
-							plaintext[i] = 0 ^ ciphertext[i]
-						elif len(ciphertext)-1 < i:
-							plaintext[i] = output[i] ^ 0
-						elif len(output)-1 < i and len(ciphertext) < i:
-							plaintext[i] = 0 ^ 0
-						else:
-							plaintext[i] = output[i] ^ ciphertext[i]
-					for k in range(end-start):
-						stringOut += chr(plaintext[k])
-					iput = output
-				elif mode == self.modeOfOperation["CBC"]:
-					output = self.aes.decrypt(ciphertext, key, size)
-					for i in range(16):
-						if firstRound:
-							plaintext[i] = IV[i] ^ output[i]
-						else:
-							plaintext[i] = iput[i] ^ output[i]
-					firstRound = False
-					if originalsize is not None and originalsize < end:
-						for k in range(originalsize-start):
-							stringOut += chr(plaintext[k])
-					else:
-						for k in range(end-start):
-							stringOut += chr(plaintext[k])
-					iput = ciphertext
-		return stringOut
-
-# end of aes.py code
-
-# pywallet crypter implementation
-
-crypter = None
-
-try:
-	from Crypto.Cipher import AES
-	crypter = 'pycrypto'
-except:
-	pass
-
-class Crypter_pycrypto( object ):
-	def SetKeyFromPassphrase(self, vKeyData, vSalt, nDerivIterations, nDerivationMethod):
-		if nDerivationMethod != 0:
-			return 0
-		data = vKeyData + vSalt
-		for i in xrange(nDerivIterations):
-			data = hashlib.sha512(data).digest()
-		self.SetKey(data[0:32])
-		self.SetIV(data[32:32+16])
-		return len(data)
-
-	def SetKey(self, key):
-		self.chKey = key
-
-	def SetIV(self, iv):
-		self.chIV = iv[0:16]
-
-	def Encrypt(self, data):
-		return AES.new(self.chKey,AES.MODE_CBC,self.chIV).encrypt(data)[0:32]
-
-	def Decrypt(self, data):
-		return AES.new(self.chKey,AES.MODE_CBC,self.chIV).decrypt(data)[0:32]
-
-try:
-	if not crypter:
-		import ctypes
-		import ctypes.util
-		ssl = ctypes.cdll.LoadLibrary (ctypes.util.find_library ('ssl') or 'libeay32')
-		crypter = 'ssl'
-except:
-	pass
-
-class Crypter_ssl(object):
-	def __init__(self):
-		self.chKey = ctypes.create_string_buffer (32)
-		self.chIV = ctypes.create_string_buffer (16)
-
-	def SetKeyFromPassphrase(self, vKeyData, vSalt, nDerivIterations, nDerivationMethod):
-		if nDerivationMethod != 0:
-			return 0
-		strKeyData = ctypes.create_string_buffer (vKeyData)
-		chSalt = ctypes.create_string_buffer (vSalt)
-		return ssl.EVP_BytesToKey(ssl.EVP_aes_256_cbc(), ssl.EVP_sha512(), chSalt, strKeyData,
-			len(vKeyData), nDerivIterations, ctypes.byref(self.chKey), ctypes.byref(self.chIV))
-
-	def SetKey(self, key):
-		self.chKey = ctypes.create_string_buffer(key)
-
-	def SetIV(self, iv):
-		self.chIV = ctypes.create_string_buffer(iv)
-
-	def Encrypt(self, data):
-		buf = ctypes.create_string_buffer(len(data) + 16)
-		written = ctypes.c_int(0)
-		final = ctypes.c_int(0)
-		ctx = ssl.EVP_CIPHER_CTX_new()
-		ssl.EVP_CIPHER_CTX_init(ctx)
-		ssl.EVP_EncryptInit_ex(ctx, ssl.EVP_aes_256_cbc(), None, self.chKey, self.chIV)
-		ssl.EVP_EncryptUpdate(ctx, buf, ctypes.byref(written), data, len(data))
-		output = buf.raw[:written.value]
-		ssl.EVP_EncryptFinal_ex(ctx, buf, ctypes.byref(final))
-		output += buf.raw[:final.value]
-		return output
-
-	def Decrypt(self, data):
-		buf = ctypes.create_string_buffer(len(data) + 16)
-		written = ctypes.c_int(0)
-		final = ctypes.c_int(0)
-		ctx = ssl.EVP_CIPHER_CTX_new()
-		ssl.EVP_CIPHER_CTX_init(ctx)
-		ssl.EVP_DecryptInit_ex(ctx, ssl.EVP_aes_256_cbc(), None, self.chKey, self.chIV)
-		ssl.EVP_DecryptUpdate(ctx, buf, ctypes.byref(written), data, len(data))
-		output = buf.raw[:written.value]
-		ssl.EVP_DecryptFinal_ex(ctx, buf, ctypes.byref(final))
-		output += buf.raw[:final.value]
-		return output
-
-class Crypter_pure(object):
-	def __init__(self):
-		self.m = AESModeOfOperation()
-		self.cbc = self.m.modeOfOperation["CBC"]
-		self.sz = self.m.aes.keySize["SIZE_256"]
-
-	def SetKeyFromPassphrase(self, vKeyData, vSalt, nDerivIterations, nDerivationMethod):
-		if nDerivationMethod != 0:
-			return 0
-		data = vKeyData + vSalt
-		for i in xrange(nDerivIterations):
-			data = hashlib.sha512(data).digest()
-		self.SetKey(data[0:32])
-		self.SetIV(data[32:32+16])
-		return len(data)
-
-	def SetKey(self, key):
-		self.chKey = [ord(i) for i in key]
-
-	def SetIV(self, iv):
-		self.chIV = [ord(i) for i in iv]
-
-	def Encrypt(self, data):
-		mode, size, cypher = self.m.encrypt(data, self.cbc, self.chKey, self.sz, self.chIV)
-		return ''.join(map(chr, cypher))
-
-	def Decrypt(self, data):
-		chData = [ord(i) for i in data]
-		return self.m.decrypt(chData, self.sz, self.cbc, self.chKey, self.sz, self.chIV)
-
-# secp256k1
-
-_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
-_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
-_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
-_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
-_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
-_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
-
-# python-ecdsa code (EC_KEY implementation)
-
-class CurveFp( object ):
-	def __init__( self, p, a, b ):
-		self.__p = p
-		self.__a = a
-		self.__b = b
-
-	def p( self ):
-		return self.__p
-
-	def a( self ):
-		return self.__a
-
-	def b( self ):
-		return self.__b
-
-	def contains_point( self, x, y ):
-		return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0
-
-class Point( object ):
-	def __init__( self, curve, x, y, order = None ):
-		self.__curve = curve
-		self.__x = x
-		self.__y = y
-		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
-		assert self.__curve == other.__curve
-		if self.__x == other.__x:
-			if ( self.__y + other.__y ) % self.__curve.p() == 0:
-				return INFINITY
-			else:
-				return self.double()
-
-		p = self.__curve.p()
-		l = ( ( other.__y - self.__y ) * \
-					inverse_mod( other.__x - self.__x, p ) ) % p
-		x3 = ( l * l - self.__x - other.__x ) % p
-		y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
-		return Point( self.__curve, x3, y3 )
-
-	def __mul__( self, other ):
-		def leftmost_bit( x ):
-			assert x > 0
-			result = 1L
-			while result <= x: result = 2 * result
-			return result / 2
-
-		e = other
-		if self.__order: e = e % self.__order
-		if e == 0: return INFINITY
-		if self == INFINITY: return INFINITY
-		assert e > 0
-		e3 = 3 * e
-		negative_self = Point( self.__curve, self.__x, -self.__y, self.__order )
-		i = leftmost_bit( e3 ) / 2
-		result = self
-		while i > 1:
-			result = result.double()
-			if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self
-			if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self
-			i = i / 2
-		return result
-
-	def __rmul__( self, other ):
-		return self * other
-
-	def __str__( self ):
-		if self == INFINITY: return "infinity"
-		return "(%d,%d)" % ( self.__x, self.__y )
-
-	def double( self ):
-		if self == INFINITY:
-			return INFINITY
-
-		p = self.__curve.p()
-		a = self.__curve.a()
-		l = ( ( 3 * self.__x * self.__x + a ) * \
-					inverse_mod( 2 * self.__y, p ) ) % p
-		x3 = ( l * l - 2 * self.__x ) % p
-		y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
-		return Point( self.__curve, x3, y3 )
-
-	def x( self ):
-		return self.__x
-
-	def y( self ):
-		return self.__y
-
-	def curve( self ):
-		return self.__curve
-
-	def order( self ):
-		return self.__order
-
-INFINITY = Point( None, None, None )
-
-def inverse_mod( a, m ):
-	if a < 0 or m <= a: a = a % m
-	c, d = a, m
-	uc, vc, ud, vd = 1, 0, 0, 1
-	while c != 0:
-		q, c, d = divmod( d, c ) + ( c, )
-		uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc
-	assert d == 1
-	if ud > 0: return ud
-	else: return ud + m
-
-class Signature( object ):
-	def __init__( self, r, s ):
-		self.r = r
-		self.s = s
-
-class Public_key( object ):
-	def __init__( self, generator, point ):
-		self.curve = generator.curve()
-		self.generator = generator
-		self.point = point
-		n = generator.order()
-		if not n:
-			raise RuntimeError, "Generator point must have order."
-		if not n * point == INFINITY:
-			raise RuntimeError, "Generator point order is bad."
-		if point.x() < 0 or n <= point.x() or point.y() < 0 or n <= point.y():
-			raise RuntimeError, "Generator point has x or y out of range."
-
-	def verifies( self, hash, signature ):
-		G = self.generator
-		n = G.order()
-		r = signature.r
-		s = signature.s
-		if r < 1 or r > n-1: return False
-		if s < 1 or s > n-1: return False
-		c = inverse_mod( s, n )
-		u1 = ( hash * c ) % n
-		u2 = ( r * c ) % n
-		xy = u1 * G + u2 * self.point
-		v = xy.x() % n
-		return v == r
-
-class Private_key( object ):
-	def __init__( self, public_key, secret_multiplier ):
-		self.public_key = public_key
-		self.secret_multiplier = secret_multiplier
-
-	def der( self ):
-		hex_der_key = '06052b8104000a30740201010420' + \
-			'%064x' % self.secret_multiplier + \
-			'a00706052b8104000aa14403420004' + \
-			'%064x' % self.public_key.point.x() + \
-			'%064x' % self.public_key.point.y()
-		return hex_der_key.decode('hex')
-
-	def sign( self, hash, random_k ):
-		G = self.public_key.generator
-		n = G.order()
-		k = random_k % n
-		p1 = k * G
-		r = p1.x()
-		if r == 0: raise RuntimeError, "amazingly unlucky random number r"
-		s = ( inverse_mod( k, n ) * \
-					( hash + ( self.secret_multiplier * r ) % n ) ) % n
-		if s == 0: raise RuntimeError, "amazingly unlucky random number s"
-		return Signature( r, s )
-
-class EC_KEY(object):
-	def __init__( self, secret ):
-		curve = CurveFp( _p, _a, _b )
-		generator = Point( curve, _Gx, _Gy, _r )
-		self.pubkey = Public_key( generator, generator * secret )
-		self.privkey = Private_key( self.pubkey, secret )
-		self.secret = secret
-
-# end of python-ecdsa code
-
-# pywallet openssl private key implementation
-
-def i2d_ECPrivateKey(pkey, compressed=False):
-	if compressed:
-		key = '3081d30201010420' + \
-			'%064x' % pkey.secret + \
-			'a081a53081a2020101302c06072a8648ce3d0101022100' + \
-			'%064x' % _p + \
-			'3006040100040107042102' + \
-			'%064x' % _Gx + \
-			'022100' + \
-			'%064x' % _r + \
-			'020101a124032200'
-	else:
-		key = '308201130201010420' + \
-			'%064x' % pkey.secret + \
-			'a081a53081a2020101302c06072a8648ce3d0101022100' + \
-			'%064x' % _p + \
-			'3006040100040107044104' + \
-			'%064x' % _Gx + \
-			'%064x' % _Gy + \
-			'022100' + \
-			'%064x' % _r + \
-			'020101a144034200'
-
-	return key.decode('hex') + i2o_ECPublicKey(pkey, compressed)
-
-def i2o_ECPublicKey(pkey, compressed=False):
-	# public keys are 65 bytes long (520 bits)
-	# 0x04 + 32-byte X-coordinate + 32-byte Y-coordinate
-	# 0x00 = point at infinity, 0x02 and 0x03 = compressed, 0x04 = uncompressed
-	# compressed keys: <sign> <x> where <sign> is 0x02 if y is even and 0x03 if y is odd
-	if compressed:
-		if pkey.pubkey.point.y() & 1:
-			key = '03' + '%064x' % pkey.pubkey.point.x()
-		else:
-			key = '02' + '%064x' % pkey.pubkey.point.x()
-	else:
-		key = '04' + \
-			'%064x' % pkey.pubkey.point.x() + \
-			'%064x' % pkey.pubkey.point.y()
-
-	return key.decode('hex')
-
-# bitcointools hashes and base58 implementation
-
-def hash_160(public_key):
-	md = hashlib.new('ripemd160')
-	md.update(hashlib.sha256(public_key).digest())
-	return md.digest()
-
-def public_key_to_bc_address(public_key):
-	h160 = hash_160(public_key)
-	return hash_160_to_bc_address(h160)
-
-def hash_160_to_bc_address(h160):
-	vh160 = chr(addrtype) + h160
-	h = Hash(vh160)
-	addr = vh160 + h[0:4]
-	return b58encode(addr)
-
-def bc_address_to_hash_160(addr):
-	bytes = b58decode(addr, 25)
-	return bytes[1:21]
-
-__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
-__b58base = len(__b58chars)
-
-def b58encode(v):
-	""" encode v, which is a string of bytes, to base58.
-	"""
-
-	long_value = 0L
-	for (i, c) in enumerate(v[::-1]):
-		long_value += (256**i) * ord(c)
-
-	result = ''
-	while long_value >= __b58base:
-		div, mod = divmod(long_value, __b58base)
-		result = __b58chars[mod] + result
-		long_value = div
-	result = __b58chars[long_value] + result
-
-	# Bitcoin does a little leading-zero-compression:
-	# leading 0-bytes in the input become leading-1s
-	nPad = 0
-	for c in v:
-		if c == '\0': nPad += 1
-		else: break
-
-	return (__b58chars[0]*nPad) + result
-
-def b58decode(v, length):
-	""" decode v into a string of len bytes
-	"""
-	long_value = 0L
-	for (i, c) in enumerate(v[::-1]):
-		long_value += __b58chars.find(c) * (__b58base**i)
-
-	result = ''
-	while long_value >= 256:
-		div, mod = divmod(long_value, 256)
-		result = chr(mod) + result
-		long_value = div
-	result = chr(long_value) + result
-
-	nPad = 0
-	for c in v:
-		if c == __b58chars[0]: nPad += 1
-		else: break
-
-	result = chr(0)*nPad + result
-	if length is not None and len(result) != length:
-		return None
-
-	return result
-
-# end of bitcointools base58 implementation
-
-
-# address handling code
-
-def Hash(data):
-	return hashlib.sha256(hashlib.sha256(data).digest()).digest()
-
-def EncodeBase58Check(secret):
-	hash = Hash(secret)
-	return b58encode(secret + hash[0:4])
-
-def DecodeBase58Check(sec):
-	vchRet = b58decode(sec, None)
-	secret = vchRet[0:-4]
-	csum = vchRet[-4:]
-	hash = Hash(secret)
-	cs32 = hash[0:4]
-	if cs32 != csum:
-		return None
-	else:
-		return secret
-
-def PrivKeyToSecret(privkey):
-	if len(privkey) == 279:
-		return privkey[9:9+32]
-	else:
-		return privkey[8:8+32]
-
-def SecretToASecret(secret, compressed=False):
-	vchIn = chr((addrtype+128)&255) + secret
-	if compressed: vchIn += '\01'
-	return EncodeBase58Check(vchIn)
-
-def ASecretToSecret(sec):
-	vch = DecodeBase58Check(sec)
-	if vch and vch[0] == chr((addrtype+128)&255):
-		return vch[1:]
-	else:
-		return False
-
-def regenerate_key(sec):
-	b = ASecretToSecret(sec)
-	if not b:
-		return False
-	b = b[0:32]
-	secret = int('0x' + b.encode('hex'), 16)
-	return EC_KEY(secret)
-
-def GetPubKey(pkey, compressed=False):
-	return i2o_ECPublicKey(pkey, compressed)
-
-def GetPrivKey(pkey, compressed=False):
-	return i2d_ECPrivateKey(pkey, compressed)
-
-def GetSecret(pkey):
-	return ('%064x' % pkey.secret).decode('hex')
-
-def is_compressed(sec):
-	b = ASecretToSecret(sec)
-	return len(b) == 33
-
-# bitcointools wallet.dat handling code
-
-def create_env(db_dir):
-	db_env = DBEnv(0)
-	r = db_env.open(db_dir, (DB_CREATE|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_THREAD|DB_RECOVER))
-	return db_env
-
-def parse_CAddress(vds):
-	d = {'ip':'0.0.0.0','port':0,'nTime': 0}
-	try:
-		d['nVersion'] = vds.read_int32()
-		d['nTime'] = vds.read_uint32()
-		d['nServices'] = vds.read_uint64()
-		d['pchReserved'] = vds.read_bytes(12)
-		d['ip'] = socket.inet_ntoa(vds.read_bytes(4))
-		d['port'] = vds.read_uint16()
-	except:
-		pass
-	return d
-
-def deserialize_CAddress(d):
-	return d['ip']+":"+str(d['port'])
-
-def parse_BlockLocator(vds):
-	d = { 'hashes' : [] }
-	nHashes = vds.read_compact_size()
-	for i in xrange(nHashes):
-		d['hashes'].append(vds.read_bytes(32))
-		return d
-
-def deserialize_BlockLocator(d):
-	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
-		return str(vds.read_boolean())
-	elif setting[0:4] == "addr": # CAddress
-		d = parse_CAddress(vds)
-		return deserialize_CAddress(d)
-	elif setting == "nTransactionFee":
-		return vds.read_int64()
-	elif setting == "nLimitProcessors":
-		return vds.read_int32()
-	return 'unknown setting'
-
-class SerializationError(Exception):
-	""" Thrown when there's a problem deserializing or serializing """
-
-class BCDataStream(object):
-	def __init__(self):
-		self.input = None
-		self.read_cursor = 0
-
-	def clear(self):
-		self.input = None
-		self.read_cursor = 0
-
-	def write(self, bytes):    # Initialize with string of bytes
-		if self.input is None:
-			self.input = bytes
-		else:
-			self.input += bytes
-
-	def map_file(self, file, start):    # Initialize with bytes from file
-		self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
-		self.read_cursor = start
-	def seek_file(self, position):
-		self.read_cursor = position
-	def close_file(self):
-		self.input.close()
-
-	def read_string(self):
-		# Strings are encoded depending on length:
-		# 0 to 252 :    1-byte-length followed by bytes (if any)
-		# 253 to 65,535 : byte'253' 2-byte-length followed by bytes
-		# 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes
-		# ... and the Bitcoin client is coded to understand:
-		# greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string
-		# ... but I don't think it actually handles any strings that big.
-		if self.input is None:
-			raise SerializationError("call write(bytes) before trying to deserialize")
-
-		try:
-			length = self.read_compact_size()
-		except IndexError:
-			raise SerializationError("attempt to read past end of buffer")
-
-		return self.read_bytes(length)
-
-	def write_string(self, string):
-		# Length-encoded as with read-string
-		self.write_compact_size(len(string))
-		self.write(string)
-
-	def read_bytes(self, length):
-		try:
-			result = self.input[self.read_cursor:self.read_cursor+length]
-			self.read_cursor += length
-			return result
-		except IndexError:
-			raise SerializationError("attempt to read past end of buffer")
-
-		return ''
-
-	def read_boolean(self): return self.read_bytes(1)[0] != chr(0)
-	def read_int16(self): return self._read_num('<h')
-	def read_uint16(self): return self._read_num('<H')
-	def read_int32(self): return self._read_num('<i')
-	def read_uint32(self): return self._read_num('<I')
-	def read_int64(self): return self._read_num('<q')
-	def read_uint64(self): return self._read_num('<Q')
-
-	def write_boolean(self, val): return self.write(chr(1) if val else chr(0))
-	def write_int16(self, val): return self._write_num('<h', val)
-	def write_uint16(self, val): return self._write_num('<H', val)
-	def write_int32(self, val): return self._write_num('<i', val)
-	def write_uint32(self, val): return self._write_num('<I', val)
-	def write_int64(self, val): return self._write_num('<q', val)
-	def write_uint64(self, val): return self._write_num('<Q', val)
-
-	def read_compact_size(self):
-		size = ord(self.input[self.read_cursor])
-		self.read_cursor += 1
-		if size == 253:
-			size = self._read_num('<H')
-		elif size == 254:
-			size = self._read_num('<I')
-		elif size == 255:
-			size = self._read_num('<Q')
-		return size
-
-	def write_compact_size(self, size):
-		if size < 0:
-			raise SerializationError("attempt to write size < 0")
-		elif size < 253:
-			self.write(chr(size))
-		elif size < 2**16:
-			self.write('\xfd')
-			self._write_num('<H', size)
-		elif size < 2**32:
-			self.write('\xfe')
-			self._write_num('<I', size)
-		elif size < 2**64:
-			self.write('\xff')
-			self._write_num('<Q', size)
-
-	def _read_num(self, format):
-		(i,) = struct.unpack_from(format, self.input, self.read_cursor)
-		self.read_cursor += struct.calcsize(format)
-		return i
-
-	def _write_num(self, format, num):
-		s = struct.pack(format, num)
-		self.write(s)
-
-def open_wallet(db_env, db_file="wallet.dat", writable=False):
-	db = DB(db_env)
-	flags = DB_THREAD | (DB_CREATE if writable else DB_RDONLY)
-	try:
-		r = db.open(db_file, "main", DB_BTREE, flags)
-	except DBError:
-		r = True
-
-	if r is not None:
-		logging.error("Couldn't open " + db_file + "/main. Try quitting Bitcoin and running this again.")
-		sys.exit(1)
-
-	return db
-
-def parse_wallet(db, item_callback):
-	kds = BCDataStream()
-	vds = BCDataStream()
-
-	for (key, value) in db.items():
-		d = { }
-
-		kds.clear(); kds.write(key)
-		vds.clear(); vds.write(value)
-
-		type = kds.read_string()
-
-		d["__key__"] = key
-		d["__value__"] = value
-		d["__type__"] = type
-
-		try:
-			if type == "tx":
-				d["tx_id"] = kds.read_bytes(32)
-			elif type == "name":
-				d['hash'] = kds.read_string()
-				d['name'] = vds.read_string()
-			elif type == "version":
-				d['version'] = vds.read_uint32()
-			elif type == "minversion":
-				d['minversion'] = vds.read_uint32()
-			elif type == "setting":
-				d['setting'] = kds.read_string()
-				d['value'] = parse_setting(d['setting'], vds)
-			elif type == "key":
-				d['public_key'] = kds.read_bytes(kds.read_compact_size())
-				d['private_key'] = vds.read_bytes(vds.read_compact_size())
-			elif type == "wkey":
-				d['public_key'] = kds.read_bytes(kds.read_compact_size())
-				d['private_key'] = vds.read_bytes(vds.read_compact_size())
-				d['created'] = vds.read_int64()
-				d['expires'] = vds.read_int64()
-				d['comment'] = vds.read_string()
-			elif type == "ckey":
-				d['public_key'] = kds.read_bytes(kds.read_compact_size())
-				d['crypted_key'] = vds.read_bytes(vds.read_compact_size())
-			elif type == "mkey":
-				d['nID'] = kds.read_int32()
-				d['crypted_key'] = vds.read_bytes(vds.read_compact_size())
-				d['salt'] = vds.read_bytes(vds.read_compact_size())
-				d['nDerivationMethod'] = vds.read_int32()
-				d['nDeriveIterations'] = vds.read_int32()
-				d['vchOtherDerivationParameters'] = vds.read_bytes(vds.read_compact_size())
-			elif type == "defaultkey":
-				d['key'] = vds.read_bytes(vds.read_compact_size())
-			elif type == "pool":
-				d['n'] = kds.read_int64()
-				d['nVersion'] = vds.read_int32()
-				d['nTime'] = vds.read_int64()
-				d['public_key'] = vds.read_bytes(vds.read_compact_size())
-			elif type == "acc":
-				d['account'] = kds.read_string()
-				d['nVersion'] = vds.read_int32()
-				d['public_key'] = vds.read_bytes(vds.read_compact_size())
-			elif type == "acentry":
-				d['account'] = kds.read_string()
-				d['n'] = kds.read_uint64()
-				d['nVersion'] = vds.read_int32()
-				d['nCreditDebit'] = vds.read_int64()
-				d['nTime'] = vds.read_int64()
-				d['otherAccount'] = vds.read_string()
-				d['comment'] = vds.read_string()
-			elif type == "bestblock":
-				d['nVersion'] = vds.read_int32()
-				d.update(parse_BlockLocator(vds))
-
-			item_callback(type, d)
-
-		except Exception, e:
-			traceback.print_exc()
-			print("ERROR parsing wallet.dat, type %s" % type)
-			print("key data in hex: %s"%key.encode('hex_codec'))
-			print("value data in hex: %s"%value.encode('hex_codec'))
-			sys.exit(1)
-
-def update_wallet(db, type, data):
-	"""Write a single item to the wallet.
-    db must be open with writable=True.
-    type and data are the type code and data dictionary as parse_wallet would
-    give to item_callback.
-    data's __key__, __value__ and __type__ are ignored; only the primary data
-    fields are used.
-	"""
-	d = data
-	kds = BCDataStream()
-	vds = BCDataStream()
-
-	# Write the type code to the key
-	kds.write_string(type)
-	vds.write("")                         # Ensure there is something
-
-	try:
-		if type == "tx":
-			raise NotImplementedError("Writing items of type 'tx'")
-			kds.write(d['tx_id'])
-		elif type == "name":
-			kds.write_string(d['hash'])
-			vds.write_string(d['name'])
-		elif type == "version":
-			vds.write_uint32(d['version'])
-		elif type == "minversion":
-			vds.write_uint32(d['minversion'])
-		elif type == "setting":
-			raise NotImplementedError("Writing items of type 'setting'")
-			kds.write_string(d['setting'])
-			#d['value'] = parse_setting(d['setting'], vds)
-		elif type == "key":
-			kds.write_string(d['public_key'])
-			vds.write_string(d['private_key'])
-		elif type == "wkey":
-			kds.write_string(d['public_key'])
-			vds.write_string(d['private_key'])
-			vds.write_int64(d['created'])
-			vds.write_int64(d['expires'])
-			vds.write_string(d['comment'])
-		elif type == "ckey":
-			kds.write_string(d['public_key'])
-			vds.write_string(d['crypted_key'])
-		elif type == "defaultkey":
-			vds.write_string(d['key'])
-		elif type == "pool":
-			kds.write_int64(d['n'])
-			vds.write_int32(d['nVersion'])
-			vds.write_int64(d['nTime'])
-			vds.write_string(d['public_key'])
-		elif type == "acc":
-			kds.write_string(d['account'])
-			vds.write_int32(d['nVersion'])
-			vds.write_string(d['public_key'])
-		elif type == "acentry":
-			kds.write_string(d['account'])
-			kds.write_uint64(d['n'])
-			vds.write_int32(d['nVersion'])
-			vds.write_int64(d['nCreditDebit'])
-			vds.write_int64(d['nTime'])
-			vds.write_string(d['otherAccount'])
-			vds.write_string(d['comment'])
-		elif type == "bestblock":
-			vds.write_int32(d['nVersion'])
-			vds.write_compact_size(len(d['hashes']))
-			for h in d['hashes']:
-				vds.write(h)
-		else:
-			print "Unknown key type: "+type
-
-		# Write the key/value pair to the database
-		db.put(kds.input, vds.input)
-
-	except Exception, e:
-		print("ERROR writing to wallet.dat, type %s"%type)
-		print("data dictionary: %r"%data)
-		traceback.print_exc()
-
-
-def read_wallet(json_db, db_env, db_file, print_wallet, print_wallet_transactions, transaction_filter):
-
-	db = open_wallet(db_env, db_file)
-
-	json_db['keys'] = []
-	json_db['pool'] = []
-	json_db['names'] = {}
-
-	def item_callback(type, d):
-
-		global password
-
-		if type == "name":
-			json_db['names'][d['hash']] = d['name']
-
-		elif type == "version":
-			json_db['version'] = d['version']
-
-		elif type == "minversion":
-			json_db['minversion'] = d['minversion']
-
-		elif type == "setting":
-			if not json_db.has_key('settings'): json_db['settings'] = {}
-			json_db["settings"][d['setting']] = d['value']
-
-		elif type == "defaultkey":
-			json_db['defaultkey'] = public_key_to_bc_address(d['key'])
-
-		elif type == "key":
-			addr = public_key_to_bc_address(d['public_key'])
-			compressed = d['public_key'][0] != '\04'
-			sec = SecretToASecret(PrivKeyToSecret(d['private_key']), compressed)
-			private_keys.append(sec)
-			json_db['keys'].append({'addr' : addr, 'sec' : sec})
-#            json_db['keys'].append({'addr' : addr, 'sec' : sec,
-#                'secret':PrivKeyToSecret(d['private_key']).encode('hex'),
-#                'pubkey':d['public_key'].encode('hex'),
-#                'privkey':d['private_key'].encode('hex')})
-
-		elif type == "wkey":
-			if not json_db.has_key('wkey'): json_db['wkey'] = []
-			json_db['wkey']['created'] = d['created']
-
-		elif type == "ckey":
-			addr = public_key_to_bc_address(d['public_key'])
-			ckey = d['crypted_key']
-			pubkey = d['public_key']
-			json_db['keys'].append( {'addr' : addr, 'ckey': ckey.encode('hex'), 'pubkey': pubkey.encode('hex') })
-
-		elif type == "mkey":
-			mkey = {}
-			mkey['nID'] = d['nID']
-			mkey['crypted_key'] = d['crypted_key'].encode('hex')
-			mkey['salt'] = d['salt'].encode('hex')
-			mkey['nDeriveIterations'] = d['nDeriveIterations']
-			mkey['nDerivationMethod'] = d['nDerivationMethod']
-			mkey['vchOtherDerivationParameters'] = d['vchOtherDerivationParameters'].encode('hex')
-			json_db['mkey'] = mkey
-
-			if password == None and (opt.json or opt.keysforaddrs or opt.keys):
-				if opt.passwd_file:
-					from mmgen.util import get_data_from_file
-					password = get_data_from_file(opt.passwd_file).rstrip()
-				else:
-					from mmgen.util import get_bitcoind_passphrase
-					password = get_bitcoind_passphrase("Enter password: ")
-
-			if password != None:
-				global crypter
-				if crypter == 'pycrypto':
-					crypter = Crypter_pycrypto()
-				elif crypter == 'ssl':
-					crypter = Crypter_ssl()
-				else:
-					crypter = Crypter_pure()
-					logging.warning("pycrypto or libssl not found, decryption may be slow")
-				res = crypter.SetKeyFromPassphrase(password, d['salt'], d['nDeriveIterations'], d['nDerivationMethod'])
-				if res == 0:
-					logging.error("Unsupported derivation method")
-					sys.exit(1)
-				masterkey = crypter.Decrypt(d['crypted_key'])
-				crypter.SetKey(masterkey)
-
-		elif type == "pool":
-			json_db['pool'].append( {'n': d['n'], 'addr': public_key_to_bc_address(d['public_key']), 'nTime' : d['nTime'] } )
-
-		elif type == "acc":
-			json_db['acc'] = d['account']
-#			msg("Account %s (current key: %s)"%(d['account'], public_key_to_bc_address(d['public_key'])))
-
-		elif type == "acentry":
-			json_db['acentry'] = (d['account'], d['nCreditDebit'], d['otherAccount'], time.ctime(d['nTime']), d['n'], d['comment'])
-
-		elif type == "bestblock":
-			json_db['bestblock'] = d['hashes'][0][::-1].encode('hex_codec')
-
-		else:
-			json_db[type] = 'unsupported'
-
-	parse_wallet(db, item_callback)
-
-	db.close()
-
-	for k in json_db['keys']:
-		addr = k['addr']
-		if addr in json_db['names'].keys():
-			k["label"] = json_db['names'][addr]
-		else:
-			k["reserve"] = 1
-
-	if 'mkey' in json_db.keys() and password != None:
-		check = True
-		for k in json_db['keys']:
-			ckey = k['ckey'].decode('hex')
-			public_key = k['pubkey'].decode('hex')
-			crypter.SetIV(Hash(public_key))
-			secret = crypter.Decrypt(ckey)
-			compressed = public_key[0] != '\04'
-
-			if check:
-				check = False
-				pkey = EC_KEY(int('0x' + secret.encode('hex'), 16))
-				if public_key != GetPubKey(pkey, compressed):
-					logging.error("wrong password")
-					sys.exit(1)
-
-			sec = SecretToASecret(secret, compressed)
-			k['sec'] = sec
-			k['secret'] = secret.encode('hex')
-			del(k['ckey'])
-			del(k['secret'])
-			del(k['pubkey'])
-			private_keys.append(sec)
-
-	del(json_db['pool'])
-	del(json_db['names'])
-
-
-# Non-portable.  For Windows, works only if supplied filename is in current dir
-
-# main()
-
-import os.path
-infile = os.path.abspath(cmd_args[0])
-db_dir,db_file = os.path.dirname(infile),os.path.basename(infile)
-
-#	print "[%s] [%s]" % (db_dir,db_file)
-
-db_env = create_env(db_dir)
-
-read_wallet(json_db, db_env, db_file, True, True, "")
-
-if json_db.get('minversion') > max_version:
-	print "Version mismatch (must be <= %d)" % max_version
-	exit(1)
-
-wallet_addrs = [i['addr'] for i in json_db['keys']]
-
-if opt.json:
-	data = [json.dumps(json_db, sort_keys=True, indent=4)]
-	ext,what = "json","json dump"
-
-elif opt.keys:
-	data = sorted([i['sec'] for i in json_db['keys']])
-	ext,what = "keys","private keys"
-
-elif opt.addrs:
-	data = sorted([i['addr'] for i in json_db['keys']])
-	ext,what = "addrs","addresses"
-
-elif opt.keysforaddrs:
-	from mmgen.util import get_lines_from_file
-	usr_addrs = set(get_lines_from_file(opt.keysforaddrs,"addresses",trim_comments=True))
-	data = [i['sec'] for i in json_db['keys'] if i['addr'] in usr_addrs]
-	ext,what = "keys","private keys"
-	if len(data) < len(usr_addrs):
-		msg("Warning: not all requested keys found")
-
-len_arg = "%s" % len(wallet_addrs) \
-	if len(data) == len(wallet_addrs) or ext == "json" \
-		else "%s:%s" % (len(data),len(wallet_addrs))
-
-from mmgen.util import make_chksum_8,write_data_to_file
-wallet_id = make_chksum_8(str(sorted(wallet_addrs)))
-
-data = "\n".join(data) + "\n"
-
-# Output data
-of = "wd_%s[%s].%s" % (wallet_id,len_arg,ext)
-write_data_to_file(of, data, what, ask_overwrite=not opt.quiet)
-# 		outfile,
-# 		data,
-# 		desc="data",
-# 		ask_write=False,
-# 		ask_write_prompt="",
-# 		ask_write_default_yes=False,
-# 		ask_overwrite=True,
-# 		ask_tty=True,
-# 		no_tty=False,
-# 		silent=False

+ 11 - 15
mmgen/main_tool.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -21,14 +21,12 @@ mmgen-tool:  Perform various MMGen- and Bitcoin-related operations.
              Part of the MMGen suite
              Part of the MMGen suite
 """
 """
 
 
-import sys
-import mmgen.globalvars as g
-import mmgen.opt as opt
+from mmgen.common import *
 import mmgen.tool as tool
 import mmgen.tool as tool
 
 
 opts_data = {
 opts_data = {
-	'desc':    "Perform various {pnm}- and Bitcoin-related operations".format(pnm=g.proj_name),
-	'usage':   "[opts] <command> <command args>",
+	'desc':    'Perform various {pnm}- and Bitcoin-related operations'.format(pnm=g.proj_name),
+	'usage':   '[opts] <command> <command args>',
 	'options': """
 	'options': """
 -d, --outdir=       d Specify an alternate directory 'd' for output
 -d, --outdir=       d Specify an alternate directory 'd' for output
 -h, --help            Print this help message
 -h, --help            Print this help message
@@ -46,27 +44,25 @@ command
 """.format(tool.cmd_help,g.prog_name)
 """.format(tool.cmd_help,g.prog_name)
 }
 }
 
 
-cmd_args = opt.opts.init(opts_data,
+cmd_args = opts.init(opts_data,
 	add_opts=[
 	add_opts=[
-		"no_keyconv",
-		"hidden_incog_input_params",
-		"in_fmt"
+		'no_keyconv',
+		'hidden_incog_input_params',
+		'in_fmt'
 		])
 		])
 
 
 if len(cmd_args) < 1:
 if len(cmd_args) < 1:
-	opt.opts.usage()
+	opts.usage()
 	sys.exit(1)
 	sys.exit(1)
 
 
 command = cmd_args.pop(0)
 command = cmd_args.pop(0)
 
 
 if command not in tool.cmd_data:
 if command not in tool.cmd_data:
-	from mmgen.util import msg
-	msg("'%s': No such command" % command)
-	sys.exit(1)
+	die(1,"'%s': no such command" % command)
 
 
 if cmd_args and cmd_args[0] == '--help':
 if cmd_args and cmd_args[0] == '--help':
 	tool.tool_usage(g.prog_name, command)
 	tool.tool_usage(g.prog_name, command)
-	sys.exit(0)
+	sys.exit()
 
 
 args,kwargs = tool.process_args(g.prog_name, command, cmd_args)
 args,kwargs = tool.process_args(g.prog_name, command, cmd_args)
 
 

+ 137 - 152
mmgen/main_txcreate.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -21,19 +21,17 @@ mmgen-txcreate: Create a Bitcoin transaction to and from MMGen- or non-MMGen
                 inputs and outputs
                 inputs and outputs
 """
 """
 
 
-import sys
 from decimal import Decimal
 from decimal import Decimal
 
 
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import dmsg
+from mmgen.common import *
 from mmgen.tx import *
 from mmgen.tx import *
+from mmgen.term import get_char
 
 
 pnm = g.proj_name
 pnm = g.proj_name
 
 
 opts_data = {
 opts_data = {
-	'desc':    "Create a BTC transaction with outputs to specified addresses",
-	'usage':   "[opts]  <addr,amt> ... [change addr] [addr file] ...",
+	'desc':    'Create a BTC transaction with outputs to specified addresses',
+	'usage':   '[opts]  <addr,amt> ... [change addr] [addr file] ...',
 	'options': """
 	'options': """
 -h, --help            Print this help message
 -h, --help            Print this help message
 -c, --comment-file= f Source the transaction's comment from file 'f'
 -c, --comment-file= f Source the transaction's comment from file 'f'
@@ -63,8 +61,8 @@ with no amount on the command line.
 
 
 wmsg = {
 wmsg = {
 	'too_many_acct_addresses': """
 	'too_many_acct_addresses': """
-ERROR: More than one address found for account: "%s".
-Your "wallet.dat" file appears to have been altered by a non-{pnm} program.
+ERROR: More than one address found for account: '%s'.
+Your 'wallet.dat' file appears to have been altered by a non-{pnm} program.
 Please restore your tracking wallet from a backup or create a new one and
 Please restore your tracking wallet from a backup or create a new one and
 re-import your addresses.
 re-import your addresses.
 """.strip().format(pnm=pnm),
 """.strip().format(pnm=pnm),
@@ -106,44 +104,44 @@ was specified.
 
 
 def format_unspent_outputs_for_printing(out,sort_info,total):
 def format_unspent_outputs_for_printing(out,sort_info,total):
 
 
-	pfs  = " %-4s %-67s %-34s %-12s %-13s %-8s %-10s %s"
-	pout = [pfs % ("Num","TX id,Vout","Address","{pnm} ID".format(pnm=pnm),
-		"Amount (BTC)","Conf.","Age (days)", "Comment")]
+	pfs  = ' %-4s %-67s %-34s %-12s %-13s %-8s %-10s %s'
+	pout = [pfs % ('Num','TX id,Vout','Address','{pnm} ID'.format(pnm=pnm),
+		'Amount (BTC)','Conf.','Age (days)', 'Comment')]
 
 
 	for n,i in enumerate(out):
 	for n,i in enumerate(out):
-		addr = "=" if i.skip == "addr" and "grouped" in sort_info else i.address
-		tx = " " * 63 + "=" \
-			if i.skip == "txid" and "grouped" in sort_info else str(i.txid)
+		addr = '=' if i['skip'] == 'addr' and 'grouped' in sort_info else i['address']
+		tx = ' ' * 63 + '=' \
+			if i['skip'] == 'txid' and 'grouped' in sort_info else str(i['txid'])
 
 
-		s = pfs % (str(n+1)+")", tx+","+str(i.vout),addr,
-				i.mmid,i.amt,i.confirmations,i.days,i.comment)
+		s = pfs % (str(n+1)+')', tx+','+str(i['vout']),addr,
+				i['mmid'],i['amt'],i['confirmations'],i['days'],i['comment'])
 		pout.append(s.rstrip())
 		pout.append(s.rstrip())
 
 
 	return \
 	return \
-"Unspent outputs ({} UTC)\nSort order: {}\n\n{}\n\nTotal BTC: {}\n".format(
-		make_timestr(), " ".join(sort_info), "\n".join(pout), total
+'Unspent outputs ({} UTC)\nSort order: {}\n\n{}\n\nTotal BTC: {}\n'.format(
+		make_timestr(), ' '.join(sort_info), '\n'.join(pout), total
 	)
 	)
 
 
 
 
 def sort_and_view(unspent):
 def sort_and_view(unspent):
 
 
-	def s_amt(i):   return i.amount
-	def s_txid(i):  return "%s %03s" % (i.txid,i.vout)
-	def s_addr(i):  return i.address
-	def s_age(i):   return i.confirmations
+	def s_amt(i):   return i['amount']
+	def s_txid(i):  return '%s %03s' % (i['txid'],i['vout'])
+	def s_addr(i):  return i['address']
+	def s_age(i):   return i['confirmations']
 	def s_mmgen(i):
 	def s_mmgen(i):
-		if i.mmid:
-			return "{}:{:>0{w}}".format(
-				*i.mmid.split(":"), w=g.mmgen_idx_max_digits)
-		else: return "G" + i.comment
+		if i['mmid']:
+			return '{}:{:>0{w}}'.format(
+				*i['mmid'].split(':'), w=g.mmgen_idx_max_digits)
+		else: return 'G' + i['comment']
 
 
-	sort,group,show_days,show_mmaddr,reverse = "age",False,False,True,True
+	sort,group,show_days,show_mmaddr,reverse = 'age',False,False,True,True
 	unspent.sort(key=s_age,reverse=reverse) # Reverse age sort by default
 	unspent.sort(key=s_age,reverse=reverse) # Reverse age sort by default
 
 
-	total = trim_exponent(sum([i.amount for i in unspent]))
-	max_acct_len = max([len(i.mmid+" "+i.comment) for i in unspent])
+	total = trim_exponent(sum([i['amount'] for i in unspent]))
+	max_acct_len = max([len(i['mmid']+' '+i['comment']) for i in unspent])
 
 
-	hdr_fmt   = "UNSPENT OUTPUTS (sort order: %s)  Total BTC: %s"
+	hdr_fmt   = 'UNSPENT OUTPUTS (sort order: %s)  Total BTC: %s'
 	options_msg = """
 	options_msg = """
 Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr
 Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr
 Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
@@ -151,85 +149,84 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 	prompt = \
 	prompt = \
 "('q' = quit sorting, 'p' = print to file, 'v' = pager view, 'w' = wide view): "
 "('q' = quit sorting, 'p' = print to file, 'v' = pager view, 'w' = wide view): "
 
 
-	mmid_w = max(len(i.mmid) for i in unspent)
+	mmid_w = max(len(i['mmid']) for i in unspent)
 	from copy import deepcopy
 	from copy import deepcopy
 	from mmgen.term import get_terminal_size
 	from mmgen.term import get_terminal_size
 
 
-	written_to_file_msg = ""
-	msg("")
+	written_to_file_msg = ''
+	msg('')
 
 
 	while True:
 	while True:
 		cols = get_terminal_size()[0]
 		cols = get_terminal_size()[0]
 		if cols < g.min_screen_width:
 		if cols < g.min_screen_width:
-			msg(
-	"{pnl}-txcreate requires a screen at least {w} characters wide".format(
+			die(2,
+	'{pnl}-txcreate requires a screen at least {w} characters wide'.format(
 					pnl=pnm.lower(),w=g.min_screen_width))
 					pnl=pnm.lower(),w=g.min_screen_width))
-			sys.exit(2)
 
 
 		addr_w = min(34+((1+max_acct_len) if show_mmaddr else 0),cols-46)
 		addr_w = min(34+((1+max_acct_len) if show_mmaddr else 0),cols-46)
 		acct_w   = min(max_acct_len, max(24,int(addr_w-10)))
 		acct_w   = min(max_acct_len, max(24,int(addr_w-10)))
 		btaddr_w = addr_w - acct_w - 1
 		btaddr_w = addr_w - acct_w - 1
 		tx_w = max(11,min(64, cols-addr_w-32))
 		tx_w = max(11,min(64, cols-addr_w-32))
-		txdots = "..." if tx_w < 64 else ""
-		fs = " %-4s %-" + str(tx_w) + "s %-2s %-" + str(addr_w) + "s %-13s %-s"
-		table_hdr = fs % ("Num","TX id  Vout","","Address","Amount (BTC)",
-							"Age(d)" if show_days else "Conf.")
+		txdots = ('','...')[tx_w < 64]
+		fs = ' %-4s %-' + str(tx_w) + 's %-2s %-' + str(addr_w) + 's %-13s %-s'
+		table_hdr = fs % ('Num','TX id  Vout','','Address','Amount (BTC)',
+							('Conf.','Age(d)')[show_days])
 
 
 		unsp = deepcopy(unspent)
 		unsp = deepcopy(unspent)
-		for i in unsp: i.skip = ""
-		if group and (sort == "address" or sort == "txid"):
+		for i in unsp: i['skip'] = ''
+		if group and (sort == 'address' or sort == 'txid'):
 			for a,b in [(unsp[i],unsp[i+1]) for i in range(len(unsp)-1)]:
 			for a,b in [(unsp[i],unsp[i+1]) for i in range(len(unsp)-1)]:
-				if sort == "address" and a.address == b.address: b.skip = "addr"
-				elif sort == "txid" and a.txid == b.txid:        b.skip = "txid"
+				if sort == 'address' and a['address'] == b['address']: b['skip'] = 'addr'
+				elif sort == 'txid' and a['txid'] == b['txid']:        b['skip'] = 'txid'
 
 
 		for i in unsp:
 		for i in unsp:
-			amt = str(trim_exponent(i.amount))
-			lfill = 3 - len(amt.split(".")[0]) if "." in amt else 3 - len(amt)
-			i.amt = " "*lfill + amt
-			i.days = int(i.confirmations * g.mins_per_block / (60*24))
-			i.age = i.days if show_days else i.confirmations
-
-			if i.skip == "addr":
-				i.addr = "|" + "." * 33
+			amt = str(trim_exponent(i['amount']))
+			lfill = 3 - len(amt.split('.')[0]) if '.' in amt else 3 - len(amt)
+			i['amt'] = ' '*lfill + amt
+			i['days'] = int(i['confirmations'] * g.mins_per_block / (60*24))
+			i['age'] = i['days'] if show_days else i['confirmations']
+
+			addr_disp = (i['address'],'|' + '.'*33)[i['skip']=='addr']
+			mmid_disp = (i['mmid'],'.'*len(i['mmid']))[i['skip']=='addr']
+
+			if show_mmaddr:
+				dots = ('','..')[btaddr_w < len(i['address'])]
+				i['addr'] = '%s%s %s' % (
+					addr_disp[:btaddr_w-len(dots)],
+					dots, (
+					('{:<{w}} '.format(mmid_disp,w=mmid_w) if i['mmid'] else '')
+						+ i['comment'])[:acct_w]
+					)
 			else:
 			else:
-				if show_mmaddr:
-					dots = ".." if btaddr_w < len(i.address) else ""
-					i.addr = "%s%s %s" % (
-						i.address[:btaddr_w-len(dots)],
-						dots, (
-						("{:<{w}} ".format(i.mmid,w=mmid_w) if i.mmid else "")
-							+ i.comment)[:acct_w]
-						)
-				else:
-					i.addr = i.address
-
-			i.tx = " " * (tx_w-4) + "|..." if i.skip == "txid" \
-					else i.txid[:tx_w-len(txdots)]+txdots
-
-		sort_info = ["reverse"] if reverse else []
-		sort_info.append(sort if sort else "unsorted")
-		if group and (sort == "address" or sort == "txid"):
-			sort_info.append("grouped")
-
-		out  = [hdr_fmt % (" ".join(sort_info), total), table_hdr]
-		out += [fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,i.age)
+				i['addr'] = addr_disp
+
+			i['tx'] = ' ' * (tx_w-4) + '|...' if i['skip'] == 'txid' \
+					else i['txid'][:tx_w-len(txdots)]+txdots
+
+		sort_info = ([],['reverse'])[reverse]
+		sort_info.append(sort if sort else 'unsorted')
+		if group and (sort == 'address' or sort == 'txid'):
+			sort_info.append('grouped')
+
+		out  = [hdr_fmt % (' '.join(sort_info), total), table_hdr]
+		out += [fs % (str(n+1)+')',i['tx'],i['vout'],i['addr'],i['amt'],i['age'])
 					for n,i in enumerate(unsp)]
 					for n,i in enumerate(unsp)]
 
 
-		msg("\n".join(out) +"\n\n" + written_to_file_msg + options_msg)
-		written_to_file_msg = ""
+		msg('\n'.join(out) +'\n\n' + written_to_file_msg + options_msg)
+		written_to_file_msg = ''
 
 
 		skip_prompt = False
 		skip_prompt = False
 
 
 		while True:
 		while True:
-			reply = get_char(prompt, immed_chars="atDdAMrgmeqpvw")
+			reply = get_char(prompt, immed_chars='atDdAMrgmeqpvw')
 
 
-			if   reply == 'a': unspent.sort(key=s_amt);  sort = "amount"
-			elif reply == 't': unspent.sort(key=s_txid); sort = "txid"
+			if   reply == 'a': unspent.sort(key=s_amt);  sort = 'amount'
+			elif reply == 't': unspent.sort(key=s_txid); sort = 'txid'
 			elif reply == 'D': show_days = not show_days
 			elif reply == 'D': show_days = not show_days
-			elif reply == 'd': unspent.sort(key=s_addr); sort = "address"
-			elif reply == 'A': unspent.sort(key=s_age);  sort = "age"
+			elif reply == 'd': unspent.sort(key=s_addr); sort = 'address'
+			elif reply == 'A': unspent.sort(key=s_age);  sort = 'age'
 			elif reply == 'M':
 			elif reply == 'M':
-				unspent.sort(key=s_mmgen); sort = "mmgen"
+				unspent.sort(key=s_mmgen); sort = 'mmgen'
 				show_mmaddr = True
 				show_mmaddr = True
 			elif reply == 'r':
 			elif reply == 'r':
 				unspent.reverse()
 				unspent.reverse()
@@ -240,23 +237,23 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 			elif reply == 'q': pass
 			elif reply == 'q': pass
 			elif reply == 'p':
 			elif reply == 'p':
 				d = format_unspent_outputs_for_printing(unsp,sort_info,total)
 				d = format_unspent_outputs_for_printing(unsp,sort_info,total)
-				of = "listunspent[%s].out" % ",".join(sort_info)
-				write_data_to_file(of,d,"unspent outputs listing")
+				of = 'listunspent[%s].out' % ','.join(sort_info)
+				write_data_to_file(of,d,'unspent outputs listing')
 				written_to_file_msg = "Data written to '%s'\n\n" % of
 				written_to_file_msg = "Data written to '%s'\n\n" % of
 			elif reply == 'v':
 			elif reply == 'v':
-				do_pager("\n".join(out))
+				do_pager('\n'.join(out))
 				continue
 				continue
 			elif reply == 'w':
 			elif reply == 'w':
 				data = format_unspent_outputs_for_printing(unsp,sort_info,total)
 				data = format_unspent_outputs_for_printing(unsp,sort_info,total)
 				do_pager(data)
 				do_pager(data)
 				continue
 				continue
 			else:
 			else:
-				msg("\nInvalid input")
+				msg('\nInvalid input')
 				continue
 				continue
 
 
 			break
 			break
 
 
-		msg("\n")
+		msg('\n')
 		if reply == 'q': break
 		if reply == 'q': break
 
 
 	return tuple(unspent)
 	return tuple(unspent)
@@ -274,24 +271,23 @@ def select_outputs(unspent,prompt):
 		if not selected: continue
 		if not selected: continue
 
 
 		if selected[-1] > len(unspent):
 		if selected[-1] > len(unspent):
-			msg("Inputs must be less than %s" % len(unspent))
+			msg('Inputs must be less than %s' % len(unspent))
 			continue
 			continue
 
 
 		return selected
 		return selected
 
 
 
 
 def mmaddr2btcaddr_unspent(unspent,mmaddr):
 def mmaddr2btcaddr_unspent(unspent,mmaddr):
-	vmsg_r("Searching for {pnm} address {m} in wallet...".format(pnm=pnm,m=mmaddr))
-	m = [u for u in unspent if u.mmid == mmaddr]
+	vmsg_r('Searching for {pnm} address {m} in wallet...'.format(pnm=pnm,m=mmaddr))
+	m = [u for u in unspent if u['mmid'] == mmaddr]
 	if len(m) == 0:
 	if len(m) == 0:
-		vmsg("not found")
-		return "",""
+		vmsg('not found')
+		return '',''
 	elif len(m) > 1:
 	elif len(m) > 1:
-		msg(wmsg['too_many_acct_addresses'] % acct); sys.exit(2)
+		die(2,wmsg['too_many_acct_addresses'] % acct)
 	else:
 	else:
-		vmsg("success (%s)" % m[0].address)
+		vmsg('success (%s)' % m[0].address)
 		return m[0].address, m[0].comment
 		return m[0].address, m[0].comment
-	sys.exit()
 
 
 
 
 def mmaddr2btcaddr(c,mmaddr,ail_w,ail_f):
 def mmaddr2btcaddr(c,mmaddr,ail_w,ail_f):
@@ -304,14 +300,12 @@ def mmaddr2btcaddr(c,mmaddr,ail_w,ail_f):
 			btcaddr = ail_f.mmaddr2btcaddr(mmaddr)
 			btcaddr = ail_f.mmaddr2btcaddr(mmaddr)
 			if btcaddr:
 			if btcaddr:
 				msg(wmsg['addr_in_addrfile_only'].format(mmgenaddr=mmaddr))
 				msg(wmsg['addr_in_addrfile_only'].format(mmgenaddr=mmaddr))
-				if not keypress_confirm("Continue anyway?"):
+				if not keypress_confirm('Continue anyway?'):
 					sys.exit(1)
 					sys.exit(1)
 			else:
 			else:
-				msg(wmsg['addr_not_found'].format(pnm=pnm,mmgenaddr=mmaddr))
-				sys.exit(2)
+				die(2,wmsg['addr_not_found'].format(pnm=pnm,mmgenaddr=mmaddr))
 		else:
 		else:
-			msg(wmsg['addr_not_found_no_addrfile'].format(pnm=pnm,mmgenaddr=mmaddr))
-			sys.exit(2)
+			die(2,wmsg['addr_not_found_no_addrfile'].format(pnm=pnm,mmgenaddr=mmaddr))
 
 
 	return btcaddr
 	return btcaddr
 
 
@@ -323,7 +317,7 @@ def make_b2m_map(inputs_data,tx_out,ail_w,ail_f):
 	d.update(ail_f.make_reverse_dict(tx_out.keys()))
 	d.update(ail_f.make_reverse_dict(tx_out.keys()))
 	return d
 	return d
 
 
-cmd_args = opt.opts.init(opts_data)
+cmd_args = opts.init(opts_data)
 
 
 if opt.comment_file:
 if opt.comment_file:
 	comment = get_tx_comment_from_file(opt.comment_file)
 	comment = get_tx_comment_from_file(opt.comment_file)
@@ -333,7 +327,7 @@ c = connect_to_bitcoind()
 if not opt.info:
 if not opt.info:
 	do_license_msg(immed=True)
 	do_license_msg(immed=True)
 
 
-	tx_out,change_addr = {},""
+	tx_out,change_addr = {},''
 
 
 	addrfiles = [a for a in cmd_args if get_extension(a) == g.addrfile_ext]
 	addrfiles = [a for a in cmd_args if get_extension(a) == g.addrfile_ext]
 	cmd_args = set(cmd_args) - set(addrfiles)
 	cmd_args = set(cmd_args) - set(addrfiles)
@@ -347,131 +341,122 @@ if not opt.info:
 	ail_w = AddrInfoList(bitcoind_connection=c)
 	ail_w = AddrInfoList(bitcoind_connection=c)
 
 
 	for a in cmd_args:
 	for a in cmd_args:
-		if "," in a:
-			a1,a2 = split2(a,",")
+		if ',' in a:
+			a1,a2 = split2(a,',')
 			if is_btc_addr(a1):
 			if is_btc_addr(a1):
 				btcaddr = a1
 				btcaddr = a1
 			elif is_mmgen_addr(a1):
 			elif is_mmgen_addr(a1):
 				btcaddr = mmaddr2btcaddr(c,a1,ail_w,ail_f)
 				btcaddr = mmaddr2btcaddr(c,a1,ail_w,ail_f)
 			else:
 			else:
-				msg("%s: unrecognized subargument in argument '%s'" % (a1,a))
-				sys.exit(2)
+				die(2,"%s: unrecognized subargument in argument '%s'" % (a1,a))
 
 
 			ret = normalize_btc_amt(a2)
 			ret = normalize_btc_amt(a2)
 			if ret:
 			if ret:
 				tx_out[btcaddr] = ret
 				tx_out[btcaddr] = ret
 			else:
 			else:
-				msg("%s: invalid amount in argument '%s'" % (a2,a))
-				sys.exit(2)
+				die(2,"%s: invalid amount in argument '%s'" % (a2,a))
 		elif is_mmgen_addr(a) or is_btc_addr(a):
 		elif is_mmgen_addr(a) or is_btc_addr(a):
 			if change_addr:
 			if change_addr:
-				msg("ERROR: More than one change address specified: %s, %s" %
+				die(2,'ERROR: More than one change address specified: %s, %s' %
 						(change_addr, a))
 						(change_addr, a))
-				sys.exit(2)
-			change_addr = a if is_btc_addr(a) else \
-							mmaddr2btcaddr(c,a,ail_w,ail_f)
+			change_addr = a if is_btc_addr(a) else mmaddr2btcaddr(c,a,ail_w,ail_f)
 			tx_out[change_addr] = 0
 			tx_out[change_addr] = 0
 		else:
 		else:
-			msg("%s: unrecognized argument" % a)
-			sys.exit(2)
+			die(2,'%s: unrecognized argument' % a)
 
 
 	if not tx_out:
 	if not tx_out:
-		msg("At least one output must be specified on the command line")
-		sys.exit(2)
+		die(2,'At least one output must be specified on the command line')
 
 
-	tx_fee = opt.tx_fee if opt.tx_fee else g.tx_fee
+	tx_fee = (g.tx_fee,opt.tx_fee)[bool(opt.tx_fee)]
 	tx_fee = normalize_btc_amt(tx_fee)
 	tx_fee = normalize_btc_amt(tx_fee)
 	if tx_fee > g.max_tx_fee:
 	if tx_fee > g.max_tx_fee:
-		msg("Transaction fee too large: %s > %s" % (tx_fee,g.max_tx_fee))
-		sys.exit(2)
+		die(2,'Transaction fee too large: %s > %s' % (tx_fee,g.max_tx_fee))
 
 
 if g.bogus_wallet_data:  # for debugging purposes only
 if g.bogus_wallet_data:  # for debugging purposes only
-	import mmgen.rpc.data
 	us = eval(get_data_from_file(g.bogus_wallet_data))
 	us = eval(get_data_from_file(g.bogus_wallet_data))
 else:
 else:
 	us = c.listunspent()
 	us = c.listunspent()
-#	write_data_to_file("bogus_unspent.json", repr(us), "bogus unspent data")
+#	write_data_to_file('bogus_unspent.json', repr(us), 'bogus unspent data')
 #	sys.exit()
 #	sys.exit()
 
 
-if not us: msg(wmsg['no_spendable_outputs']); sys.exit(2)
+if not us:
+	die(2,wmsg['no_spendable_outputs'])
 for o in us:
 for o in us:
-	o.mmid,o.comment = parse_mmgen_label(o.account)
-	del o.account
+	o['mmid'],o['comment'] = parse_mmgen_label(o['account'])
+	del o['account']
 unspent = sort_and_view(us)
 unspent = sort_and_view(us)
 
 
-total = trim_exponent(sum([i.amount for i in unspent]))
+total = trim_exponent(sum([i['amount'] for i in unspent]))
 
 
-msg("Total unspent: %s BTC (%s outputs)" % (total, len(unspent)))
-if opt.info: sys.exit(0)
+msg('Total unspent: %s BTC (%s outputs)' % (total, len(unspent)))
+if opt.info: sys.exit()
 
 
 send_amt = sum([tx_out[i] for i in tx_out.keys()])
 send_amt = sum([tx_out[i] for i in tx_out.keys()])
-msg("Total amount to spend: %s%s" % (
-		(send_amt or "Unknown")," BTC" if send_amt else ""))
+msg('Total amount to spend: %s' % ('%s BTC'%send_amt,'Unknown')[bool(send_amt)])
 
 
 while True:
 while True:
 	sel_nums = select_outputs(unspent,
 	sel_nums = select_outputs(unspent,
-			"Enter a range or space-separated list of outputs to spend: ")
-	msg("Selected output%s: %s" %
-		(("" if len(sel_nums) == 1 else "s"), " ".join(str(i) for i in sel_nums))
-	)
+			'Enter a range or space-separated list of outputs to spend: ')
+	msg('Selected output%s: %s' % (
+			('s','')[len(sel_nums)==1],
+			' '.join(str(i) for i in sel_nums)
+		))
 	sel_unspent = [unspent[i-1] for i in sel_nums]
 	sel_unspent = [unspent[i-1] for i in sel_nums]
 
 
-	mmaddrs = set([i.mmid for i in sel_unspent])
-	mmaddrs.discard("")
+	mmaddrs = set([i['mmid'] for i in sel_unspent])
+	mmaddrs.discard('')
 
 
 	if mmaddrs and len(mmaddrs) < len(sel_unspent):
 	if mmaddrs and len(mmaddrs) < len(sel_unspent):
-		msg(wmsg['mixed_inputs'] % ", ".join(sorted(mmaddrs)))
-		if not keypress_confirm("Accept?"):
+		msg(wmsg['mixed_inputs'] % ', '.join(sorted(mmaddrs)))
+		if not keypress_confirm('Accept?'):
 			continue
 			continue
 
 
-	total_in = trim_exponent(sum([i.amount for i in sel_unspent]))
+	total_in = trim_exponent(sum([i['amount'] for i in sel_unspent]))
 	change   = trim_exponent(total_in - (send_amt + tx_fee))
 	change   = trim_exponent(total_in - (send_amt + tx_fee))
 
 
 	if change >= 0:
 	if change >= 0:
-		prompt = "Transaction produces %s BTC in change.  OK?" % change
+		prompt = 'Transaction produces %s BTC in change.  OK?' % change
 		if keypress_confirm(prompt,default_yes=True):
 		if keypress_confirm(prompt,default_yes=True):
 			break
 			break
 	else:
 	else:
 		msg(wmsg['not_enough_btc'] % change)
 		msg(wmsg['not_enough_btc'] % change)
 
 
 if change > 0 and not change_addr:
 if change > 0 and not change_addr:
-	msg(wmsg['throwaway_change'] % change)
-	sys.exit(2)
+	die(2,wmsg['throwaway_change'] % change)
 
 
 if change_addr in tx_out and not change:
 if change_addr in tx_out and not change:
-	msg("Warning: Change address will be unused as transaction produces no change")
+	msg('Warning: Change address will be unused as transaction produces no change')
 	del tx_out[change_addr]
 	del tx_out[change_addr]
 
 
 for k,v in tx_out.items(): tx_out[k] = float(v)
 for k,v in tx_out.items(): tx_out[k] = float(v)
 
 
 if change > 0: tx_out[change_addr] = float(change)
 if change > 0: tx_out[change_addr] = float(change)
 
 
-tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
+tx_in = [{'txid':i['txid'], 'vout':i['vout']} for i in sel_unspent]
 
 
-dmsg("tx_in:  %s\ntx_out: %s" % (repr(tx_in),repr(tx_out)))
+dmsg('tx_in:  %s\ntx_out: %s' % (repr(tx_in),repr(tx_out)))
 
 
 if opt.comment_file:
 if opt.comment_file:
-	if keypress_confirm("Edit comment?",False):
+	if keypress_confirm('Edit comment?',False):
 		comment = get_tx_comment_from_user(comment)
 		comment = get_tx_comment_from_user(comment)
 else:
 else:
-	if keypress_confirm("Add a comment to transaction?",False):
+	if keypress_confirm('Add a comment to transaction?',False):
 		comment = get_tx_comment_from_user()
 		comment = get_tx_comment_from_user()
 	else: comment = False
 	else: comment = False
 
 
 tx_hex = c.createrawtransaction(tx_in,tx_out)
 tx_hex = c.createrawtransaction(tx_in,tx_out)
-qmsg("Transaction successfully created")
+qmsg('Transaction successfully created')
 
 
 amt = send_amt or change
 amt = send_amt or change
 tx_id = make_chksum_6(unhexlify(tx_hex)).upper()
 tx_id = make_chksum_6(unhexlify(tx_hex)).upper()
 metadata = tx_id, amt, make_timestamp()
 metadata = tx_id, amt, make_timestamp()
-sel_unspent = [i.__dict__ for i in sel_unspent]
 
 
 b2m_map = make_b2m_map(sel_unspent,tx_out,ail_w,ail_f)
 b2m_map = make_b2m_map(sel_unspent,tx_out,ail_w,ail_f)
 
 
-prompt_and_view_tx_data(c,"View decoded transaction?",
+prompt_and_view_tx_data(c,'View decoded transaction?',
 		sel_unspent,tx_hex,b2m_map,comment,metadata)
 		sel_unspent,tx_hex,b2m_map,comment,metadata)
 
 
-outfile = "tx_%s[%s].%s" % (tx_id,amt,g.rawtx_ext)
-data = make_tx_data("{} {} {}".format(*metadata),
+outfile = 'tx_%s[%s].%s' % (tx_id,amt,g.rawtx_ext)
+data = make_tx_data('{} {} {}'.format(*metadata),
 			tx_hex,sel_unspent,b2m_map,comment)
 			tx_hex,sel_unspent,b2m_map,comment)
-write_data_to_file(outfile,data,"transaction",ask_write_default_yes=False)
+write_data_to_file(outfile,data,'transaction',ask_write_default_yes=False)

+ 19 - 24
mmgen/main_txsend.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,17 +20,13 @@
 mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
 mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
 """
 """
 
 
-import sys
-
-import mmgen.globalvars as g
-import mmgen.opt as opt
+from mmgen.common import *
 from mmgen.tx import *
 from mmgen.tx import *
-from mmgen.util import *
 
 
 opts_data = {
 opts_data = {
-	'desc':    "Send a Bitcoin transaction signed by {pnm}-txsign".format(
+	'desc':    'Send a Bitcoin transaction signed by {pnm}-txsign'.format(
 					pnm=g.proj_name.lower()),
 					pnm=g.proj_name.lower()),
-	'usage':   "[opts] <signed transaction file>",
+	'usage':   '[opts] <signed transaction file>',
 	'options': """
 	'options': """
 -h, --help      Print this help message
 -h, --help      Print this help message
 -d, --outdir= d Specify an alternate directory 'd' for output
 -d, --outdir= d Specify an alternate directory 'd' for output
@@ -38,17 +34,17 @@ opts_data = {
 """
 """
 }
 }
 
 
-cmd_args = opt.opts.init(opts_data)
+cmd_args = opts.init(opts_data)
 
 
 if len(cmd_args) == 1:
 if len(cmd_args) == 1:
 	infile = cmd_args[0]; check_infile(infile)
 	infile = cmd_args[0]; check_infile(infile)
-else: opt.opts.usage()
+else: opts.usage()
 
 
 # Begin execution
 # Begin execution
 
 
 do_license_msg()
 do_license_msg()
 
 
-tx_data = get_lines_from_file(infile,"signed transaction data")
+tx_data = get_lines_from_file(infile,'signed transaction data')
 
 
 metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,infile)
 metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,infile)
 
 
@@ -56,32 +52,31 @@ qmsg("Signed transaction file '%s' is valid" % infile)
 
 
 c = connect_to_bitcoind()
 c = connect_to_bitcoind()
 
 
-prompt_and_view_tx_data(c,"View transaction data?",
+prompt_and_view_tx_data(c,'View transaction data?',
 	inputs_data,tx_hex,b2m_map,comment,metadata)
 	inputs_data,tx_hex,b2m_map,comment,metadata)
 
 
-if keypress_confirm("Edit transaction comment?"):
+if keypress_confirm('Edit transaction comment?'):
 	comment = get_tx_comment_from_user(comment)
 	comment = get_tx_comment_from_user(comment)
-	data = make_tx_data("{} {} {}".format(*metadata), tx_hex,
+	data = make_tx_data('{} {} {}'.format(*metadata), tx_hex,
 				inputs_data, b2m_map, comment)
 				inputs_data, b2m_map, comment)
-	write_data_to_file(infile,data,"signed transaction with edited comment")
+	write_data_to_file(infile,data,'signed transaction with edited comment')
 
 
 warn   = "Once this transaction is sent, there's no taking it back!"
 warn   = "Once this transaction is sent, there's no taking it back!"
-action = "broadcast this transaction to the network"
-expect =  "YES, I REALLY WANT TO DO THIS"
+action = 'broadcast this transaction to the network'
+expect =  'YES, I REALLY WANT TO DO THIS'
 
 
-if opt.quiet: warn,expect = "","YES"
+if opt.quiet: warn,expect = '','YES'
 
 
 confirm_or_exit(warn, action, expect)
 confirm_or_exit(warn, action, expect)
 
 
-msg("Sending transaction")
+msg('Sending transaction')
 
 
 try:
 try:
 	tx_id = c.sendrawtransaction(tx_hex)
 	tx_id = c.sendrawtransaction(tx_hex)
 except:
 except:
-	msg("Unable to send transaction")
-	sys.exit(3)
+	die(3,'Unable to send transaction')
 
 
-msg("Transaction sent: %s" % tx_id)
+msg('Transaction sent: %s' % tx_id)
 
 
-of = "tx_{}[{}].txid".format(*metadata[:2])
-write_data_to_file(of, tx_id+"\n","transaction ID",ask_overwrite=True)
+of = 'tx_{}[{}].txid'.format(*metadata[:2])
+write_data_to_file(of, tx_id+'\n','transaction ID',ask_overwrite=True)

+ 76 - 83
mmgen/main_txsign.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,21 +20,16 @@
 mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
 mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
 """
 """
 
 
-import sys
-
-import mmgen.globalvars as g
-import mmgen.opt as opt
+from mmgen.common import *
 from mmgen.tx import *
 from mmgen.tx import *
-from mmgen.util import do_license_msg,dmsg
 from mmgen.seed import SeedSource
 from mmgen.seed import SeedSource
 
 
-
 pnm = g.proj_name
 pnm = g.proj_name
 pnl = pnm.lower()
 pnl = pnm.lower()
 
 
 opts_data = {
 opts_data = {
-	'desc':    "Sign Bitcoin transactions generated by {pnl}-txcreate".format(pnl=pnl),
-	'usage':   "[opts] <transaction file>... [seed source]...",
+	'desc':    'Sign Bitcoin transactions generated by {pnl}-txcreate'.format(pnl=pnl),
+	'usage':   '[opts] <transaction file>... [seed source]...',
 	'options': """
 	'options': """
 -h, --help            Print this help message.
 -h, --help            Print this help message.
 -b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brain-
 -b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brain-
@@ -101,7 +96,7 @@ Seed data supplied in files must have the following extensions:
 FMT CODES:
 FMT CODES:
   {f}
   {f}
 """.format(
 """.format(
-		f="\n  ".join(SeedSource.format_fmt_codes().splitlines()),
+		f='\n  '.join(SeedSource.format_fmt_codes().splitlines()),
 		g=g,pnm=pnm,pnl=pnl
 		g=g,pnm=pnm,pnl=pnl
 	)
 	)
 }
 }
@@ -126,12 +121,11 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds):
 		if infiles:
 		if infiles:
 			ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
 			ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
 		elif opt.in_fmt:
 		elif opt.in_fmt:
-			qmsg("Need seed data for Seed ID %s" % seed_id)
+			qmsg('Need seed data for Seed ID %s' % seed_id)
 			ss = SeedSource()
 			ss = SeedSource()
-			msg("User input produced Seed ID %s" % make_chksum_8(seed))
+			msg('User input produced Seed ID %s' % make_chksum_8(seed))
 		else:
 		else:
-			msg("ERROR: No seed source found for Seed ID: %s" % seed_id)
-			sys.exit(2)
+			die(2,'ERROR: No seed source found for Seed ID: %s' % seed_id)
 
 
 		saved_seeds[ss.seed.sid] = ss.seed.data
 		saved_seeds[ss.seed.sid] = ss.seed.data
 
 
@@ -141,7 +135,7 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds):
 def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds):
 def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds):
 
 
 	seed_ids = set([i[:8] for i in mmgen_addrs])
 	seed_ids = set([i[:8] for i in mmgen_addrs])
-	vmsg("Need seed%s: %s" % (suf(seed_ids,"k")," ".join(seed_ids)))
+	vmsg('Need seed%s: %s' % (suf(seed_ids,'k'),' '.join(seed_ids)))
 	d = []
 	d = []
 
 
 	from mmgen.addr import generate_addrs
 	from mmgen.addr import generate_addrs
@@ -149,25 +143,25 @@ def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds):
 		# Returns only if seed is found
 		# Returns only if seed is found
 		seed = get_seed_for_seed_id(seed_id,infiles,saved_seeds)
 		seed = get_seed_for_seed_id(seed_id,infiles,saved_seeds)
 		addr_nums = [int(i[9:]) for i in mmgen_addrs if i[:8] == seed_id]
 		addr_nums = [int(i[9:]) for i in mmgen_addrs if i[:8] == seed_id]
-		opt.gen_what = "ka"
-		ai = generate_addrs(seed,addr_nums,source="txsign")
-		d += [("{}:{}".format(seed_id,e.idx),e.addr,e.wif) for e in ai.addrdata]
+		opt.gen_what = 'ka'
+		ai = generate_addrs(seed,addr_nums,source='txsign')
+		d += [('{}:{}'.format(seed_id,e.idx),e.addr,e.wif) for e in ai.addrdata]
 	return d
 	return d
 
 
 
 
 def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None):
 def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None):
 
 
 	if keys:
 	if keys:
-		qmsg("Passing %s key%s to bitcoind" % (len(keys),suf(keys,"k")))
-		dmsg("Keys:\n  %s" % "\n  ".join(keys))
+		qmsg('Passing %s key%s to bitcoind' % (len(keys),suf(keys,'k')))
+		dmsg('Keys:\n  %s' % '\n  '.join(keys))
 
 
-	msg_r("Signing transaction{}...".format(tx_num_str))
-	from mmgen.rpc import exceptions
+	msg_r('Signing transaction{}...'.format(tx_num_str))
+#	from mmgen.rpc import exceptions
 	try:
 	try:
 		sig_tx = c.signrawtransaction(tx_hex,sig_data,keys)
 		sig_tx = c.signrawtransaction(tx_hex,sig_data,keys)
-	except exceptions.InvalidAddressOrKey:
-		msg("failed\nInvalid address or key")
-		sys.exit(3)
+#	except exceptions.InvalidAddressOrKey:
+	except: # TODO
+		die(3,'failed\nInvalid address or key')
 
 
 	return sig_tx
 	return sig_tx
 
 
@@ -177,26 +171,27 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys):
 	try:
 	try:
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 	except:
 	except:
-		from mmgen.rpc import exceptions
-		msg("Using keys in wallet.dat as per user request")
-		prompt = "Enter passphrase for bitcoind wallet: "
+#		from mmgen.rpc import exceptions
+		msg('Using keys in wallet.dat as per user request')
+		prompt = 'Enter passphrase for bitcoind wallet: '
 		while True:
 		while True:
 			passwd = get_bitcoind_passphrase(prompt)
 			passwd = get_bitcoind_passphrase(prompt)
 
 
 			try:
 			try:
 				c.walletpassphrase(passwd, 9999)
 				c.walletpassphrase(passwd, 9999)
-			except exceptions.WalletPassphraseIncorrect:
-				msg("Passphrase incorrect")
+#			except exceptions.WalletPassphraseIncorrect:
+			except: # TODO
+				msg('Passphrase incorrect (or some other error)')
 			else:
 			else:
-				msg("Passphrase OK"); break
+				msg('Passphrase OK'); break
 
 
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 
 
-		msg("Locking wallet")
+		msg('Locking wallet')
 		try:
 		try:
 			c.walletlock()
 			c.walletlock()
 		except:
 		except:
-			msg("Failed to lock wallet")
+			msg('Failed to lock wallet')
 
 
 	return sig_tx
 	return sig_tx
 
 
@@ -204,55 +199,57 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys):
 def check_maps_from_seeds(maplist,desc,infiles,saved_seeds,return_keys=False):
 def check_maps_from_seeds(maplist,desc,infiles,saved_seeds,return_keys=False):
 
 
 	if not maplist: return []
 	if not maplist: return []
-	qmsg("Checking {pnm} -> BTC address mappings for {w}s (from seed(s))".format(
+	qmsg('Checking {pnm} -> BTC address mappings for {w}s (from seed(s))'.format(
 				pnm=pnm,w=desc))
 				pnm=pnm,w=desc))
 	d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds)
 	d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds)
 #	0=mmaddr 1=addr 2=wif
 #	0=mmaddr 1=addr 2=wif
 	m = dict([(e[0],e[1]) for e in d])
 	m = dict([(e[0],e[1]) for e in d])
 	for a,b in zip(sorted(m),sorted(maplist)):
 	for a,b in zip(sorted(m),sorted(maplist)):
 		if a != b:
 		if a != b:
-			al,bl = "generated seed:","tx file:"
-			msg(wmsg['mm2btc_mapping_error'] % (al,a,m[a],bl,b,maplist[b]))
-			sys.exit(3)
+			al,bl = 'generated seed:','tx file:'
+			die(3,wmsg['mm2btc_mapping_error'] % (al,a,m[a],bl,b,maplist[b]))
 	if return_keys:
 	if return_keys:
 		ret = [e[2] for e in d]
 		ret = [e[2] for e in d]
-		vmsg("Added %s wif key%s from seeds" % (len(ret),suf(ret,"k")))
+		vmsg('Added %s wif key%s from seeds' % (len(ret),suf(ret,'k')))
 		return ret
 		return ret
 
 
 def missing_keys_errormsg(addrs):
 def missing_keys_errormsg(addrs):
 	Msg("""
 	Msg("""
 A key file must be supplied (or use the '--use-wallet-dat' option)
 A key file must be supplied (or use the '--use-wallet-dat' option)
 for the following non-{pnm} address{suf}:\n    {l}""".format(
 for the following non-{pnm} address{suf}:\n    {l}""".format(
-	pnm=pnm, suf=suf(addrs,"a"), l="\n    ".join(addrs)).strip())
+	pnm=pnm, suf=suf(addrs,'a'), l='\n    '.join(addrs)).strip())
 
 
 
 
 def parse_mmgen_keyaddr_file():
 def parse_mmgen_keyaddr_file():
 	from mmgen.addr import AddrInfo
 	from mmgen.addr import AddrInfo
 	ai = AddrInfo(opt.mmgen_keys_from_file,has_keys=True)
 	ai = AddrInfo(opt.mmgen_keys_from_file,has_keys=True)
-	vmsg("Found %s wif key%s for Seed ID %s" %
-			(ai.num_addrs, suf(ai.num_addrs,"k"), ai.seed_id))
+	vmsg('Found %s wif key%s for Seed ID %s' %
+			(ai.num_addrs, suf(ai.num_addrs,'k'), ai.seed_id))
 	# idx: (0=addr, 1=comment 2=wif) -> mmaddr: (0=addr, 1=wif)
 	# idx: (0=addr, 1=comment 2=wif) -> mmaddr: (0=addr, 1=wif)
 	return dict(
 	return dict(
-		[("%s:%s"%(ai.seed_id,e.idx), (e.addr,e.wif)) for e in ai.addrdata])
+		[('%s:%s'%(ai.seed_id,e.idx), (e.addr,e.wif)) for e in ai.addrdata])
 
 
 
 
 def parse_keylist(from_file):
 def parse_keylist(from_file):
 	fn = opt.keys_from_file
 	fn = opt.keys_from_file
 	from mmgen.crypto import mmgen_decrypt_file_maybe
 	from mmgen.crypto import mmgen_decrypt_file_maybe
-	dec = mmgen_decrypt_file_maybe(fn,"non-{} keylist file".format(pnm))
-	# Remove possible dups from key-address file
-	keys_all = set(remove_comments(dec.splitlines())) # DOS-safe
+	dec = mmgen_decrypt_file_maybe(fn,'non-{} keylist file'.format(pnm))
+	keys_all = remove_comments(dec.splitlines()) # DOS-safe
+	# Key list could be bitcoind dump, so remove first space and everything following
+	keys_all = [k.split()[0] for k in keys_all]
+	keys_all = set(keys_all) # Remove possible dups
+	dmsg(repr(keys_all))
 	d = from_file['mmdata']
 	d = from_file['mmdata']
 	kawifs = [d[k][1] for k in d.keys()]
 	kawifs = [d[k][1] for k in d.keys()]
 	keys = [k for k in keys_all if k not in kawifs]
 	keys = [k for k in keys_all if k not in kawifs]
 	removed = len(keys_all) - len(keys)
 	removed = len(keys_all) - len(keys)
-	if removed: vmsg(wmsg['removed_dups'] % (removed,suf(removed,"k")))
+	if removed: vmsg(wmsg['removed_dups'] % (removed,suf(removed,'k')))
 	addrs = []
 	addrs = []
 	wif2addr_f = get_wif2addr_f()
 	wif2addr_f = get_wif2addr_f()
 	for n,k in enumerate(keys,1):
 	for n,k in enumerate(keys,1):
-		qmsg_r("\rGenerating addresses from keylist: %s/%s" % (n,len(keys)))
+		qmsg_r('\rGenerating addresses from keylist: %s/%s' % (n,len(keys)))
 		addrs.append(wif2addr_f(k))
 		addrs.append(wif2addr_f(k))
-	qmsg("\rGenerated addresses from keylist: %s/%s " % (n,len(keys)))
+	qmsg('\rGenerated addresses from keylist: %s/%s ' % (n,len(keys)))
 
 
 	return dict(zip(addrs,keys))
 	return dict(zip(addrs,keys))
 
 
@@ -260,7 +257,7 @@ def parse_keylist(from_file):
 # Check inputs and outputs maps against key-address file, deleting entries:
 # Check inputs and outputs maps against key-address file, deleting entries:
 def check_maps_from_kafile(imap,desc,kadata,return_keys=False):
 def check_maps_from_kafile(imap,desc,kadata,return_keys=False):
 	if not kadata: return []
 	if not kadata: return []
-	qmsg("Checking {pnm} -> BTC address mappings for {w}s (from key-address file)".format(pnm=pnm,w=desc))
+	qmsg('Checking {pnm} -> BTC address mappings for {w}s (from key-address file)'.format(pnm=pnm,w=desc))
 	ret = []
 	ret = []
 	for k in imap.keys():
 	for k in imap.keys():
 		if k in kadata.keys():
 		if k in kadata.keys():
@@ -268,12 +265,11 @@ def check_maps_from_kafile(imap,desc,kadata,return_keys=False):
 				del imap[k]
 				del imap[k]
 				ret += [kadata[k][1]]
 				ret += [kadata[k][1]]
 			else:
 			else:
-				kl,il = "key-address file:","tx file:"
-				msg(wmsg['mm2btc_mapping_error']%(kl,k,kadata[k][0],il,k,imap[k]))
-				sys.exit(2)
-	if ret: vmsg("Removed %s address%s from %ss map" % (len(ret),suf(ret,"a"),desc))
+				kl,il = 'key-address file:','tx file:'
+				die(2,wmsg['mm2btc_mapping_error']%(kl,k,kadata[k][0],il,k,imap[k]))
+	if ret: vmsg('Removed %s address%s from %ss map' % (len(ret),suf(ret,'a'),desc))
 	if return_keys:
 	if return_keys:
-		vmsg("Added %s wif key%s from %ss map" % (len(ret),suf(ret,"k"),desc))
+		vmsg('Added %s wif key%s from %ss map' % (len(ret),suf(ret,'k'),desc))
 		return ret
 		return ret
 
 
 
 
@@ -283,16 +279,16 @@ def get_keys_from_keylist(kldata,other_addrs):
 		if addr in kldata.keys():
 		if addr in kldata.keys():
 			ret += [kldata[addr]]
 			ret += [kldata[addr]]
 			other_addrs.remove(addr)
 			other_addrs.remove(addr)
-	vmsg("Added %s wif key%s from user-supplied keylist" %
-			(len(ret),suf(ret,"k")))
+	vmsg('Added %s wif key%s from user-supplied keylist' %
+			(len(ret),suf(ret,'k')))
 	return ret
 	return ret
 
 
 
 
-infiles = opt.opts.init(opts_data,add_opts=["b16"])
+infiles = opts.init(opts_data,add_opts=['b16'])
 
 
 #if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
 #if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
 
 
-if not infiles: opt.opts.usage()
+if not infiles: opts.usage()
 for i in infiles: check_infile(i)
 for i in infiles: check_infile(i)
 
 
 c = connect_to_bitcoind()
 c = connect_to_bitcoind()
@@ -310,28 +306,26 @@ if opt.mmgen_keys_from_file:
 if opt.keys_from_file:
 if opt.keys_from_file:
 	from_file['kldata'] = parse_keylist(from_file) or {}
 	from_file['kldata'] = parse_keylist(from_file) or {}
 
 
-tx_num_str = ""
+tx_num_str = ''
 for tx_num,tx_file in enumerate(tx_files,1):
 for tx_num,tx_file in enumerate(tx_files,1):
 	if len(tx_files) > 1:
 	if len(tx_files) > 1:
-		msg("\nTransaction #%s of %s:" % (tx_num,len(tx_files)))
-		tx_num_str = " #%s" % tx_num
+		msg('\nTransaction #%s of %s:' % (tx_num,len(tx_files)))
+		tx_num_str = ' #%s' % tx_num
 
 
-	m = "" if opt.tx_id else "transaction data"
+	m = ('transaction data','')[bool(opt.tx_id)]
 	tx_data = get_lines_from_file(tx_file,m)
 	tx_data = get_lines_from_file(tx_file,m)
 
 
 	metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,tx_file)
 	metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,tx_file)
 	vmsg("Successfully opened transaction file '%s'" % tx_file)
 	vmsg("Successfully opened transaction file '%s'" % tx_file)
 
 
-	if opt.tx_id:
-		msg(metadata[0])
-		sys.exit(0)
+	if opt.tx_id: die(0,metadata[0])
 
 
 	if opt.info or opt.terse_info:
 	if opt.info or opt.terse_info:
 		view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pause=False,
 		view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pause=False,
 				terse=opt.terse_info)
 				terse=opt.terse_info)
-		sys.exit(0)
+		sys.exit()
 
 
-	prompt_and_view_tx_data(c,"View data for transaction{}?".format(tx_num_str),
+	prompt_and_view_tx_data(c,'View data for transaction{}?'.format(tx_num_str),
 		inputs_data,tx_hex,b2m_map,comment,metadata)
 		inputs_data,tx_hex,b2m_map,comment,metadata)
 
 
 	# Start
 	# Start
@@ -347,20 +341,20 @@ for tx_num,tx_file in enumerate(tx_files,1):
 	omap = dict([(j[0],i) for i,j in b2m_map.items()])
 	omap = dict([(j[0],i) for i,j in b2m_map.items()])
 	sids = set([i[:8] for i in imap.keys()])
 	sids = set([i[:8] for i in imap.keys()])
 
 
-	keys += check_maps_from_kafile(imap,"input",from_file['mmdata'],True)
-	check_maps_from_kafile(omap,"output",from_file['mmdata'])
+	keys += check_maps_from_kafile(imap,'input',from_file['mmdata'],True)
+	check_maps_from_kafile(omap,'output',from_file['mmdata'])
 
 
-	keys += check_maps_from_seeds(imap,"input",seed_files,saved_seeds,True)
-	check_maps_from_seeds(omap,"output",seed_files,saved_seeds)
+	keys += check_maps_from_seeds(imap,'input',seed_files,saved_seeds,True)
+	check_maps_from_seeds(omap,'output',seed_files,saved_seeds)
 
 
 	extra_sids = set(saved_seeds.keys()) - sids
 	extra_sids = set(saved_seeds.keys()) - sids
 	if extra_sids:
 	if extra_sids:
-		msg("Unused Seed ID%s: %s" %
-			(suf(extra_sids,"k")," ".join(extra_sids)))
+		msg('Unused Seed ID%s: %s' %
+			(suf(extra_sids,'k'),' '.join(extra_sids)))
 
 
 	# Begin signing
 	# Begin signing
 	sig_data = [
 	sig_data = [
-		{"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
+		{'txid':i['txid'],'vout':i['vout'],'scriptPubKey':i['scriptPubKey']}
 			for i in inputs_data]
 			for i in inputs_data]
 
 
 	if opt.use_wallet_dat:
 	if opt.use_wallet_dat:
@@ -370,21 +364,20 @@ for tx_num,tx_file in enumerate(tx_files,1):
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 		sig_tx = sign_transaction(c,tx_hex,tx_num_str,sig_data,keys)
 
 
 	if sig_tx['complete']:
 	if sig_tx['complete']:
-		msg("OK")
-		if keypress_confirm("Edit transaction comment?"):
+		msg('OK')
+		if keypress_confirm('Edit transaction comment?'):
 			comment = get_tx_comment_from_user(comment)
 			comment = get_tx_comment_from_user(comment)
-		outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext)
+		outfile = 'tx_%s[%s].%s' % (metadata[0],metadata[1],g.sigtx_ext)
 		data = make_tx_data(
 		data = make_tx_data(
-				"{} {} {t}".format(*metadata[:2],
+				'{} {} {t}'.format(*metadata[:2],
 				t=make_timestamp()),
 				t=make_timestamp()),
 				sig_tx['hex'], inputs_data, b2m_map, comment
 				sig_tx['hex'], inputs_data, b2m_map, comment
 			)
 			)
 		write_data_to_file(
 		write_data_to_file(
 			outfile,data,
 			outfile,data,
-			"signed transaction{}".format(tx_num_str),
-			ask_write_prompt="Save signed transaction?"
+			'signed transaction{}'.format(tx_num_str),
+			ask_write_prompt='Save signed transaction?'
 		)
 		)
 	else:
 	else:
-		msg_r("failed\nSome keys were missing.  ")
-		msg("Transaction %scould not be signed." % tx_num_str)
-		sys.exit(3)
+		msg_r('failed\nSome keys were missing.  ')
+		die(3,'Transaction %scould not be signed.' % tx_num_str)

+ 41 - 42
mmgen/main_wallet.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,40 +20,39 @@
 mmgen/main_wallet:  Entry point for MMGen wallet-related scripts
 mmgen/main_wallet:  Entry point for MMGen wallet-related scripts
 """
 """
 
 
-import sys,os,re
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import die,msg,green,do_license_msg,check_infile,mdie,mmsg,qmsg,capfirst
+import os,re
+
+from mmgen.common import *
 from mmgen.seed import SeedSource
 from mmgen.seed import SeedSource
 
 
 bn = os.path.basename(sys.argv[0])
 bn = os.path.basename(sys.argv[0])
-invoked_as = re.sub(r'^wallet','',bn.split("-")[-1])
+invoked_as = re.sub(r'^wallet','',bn.split('-')[-1])
 
 
-usage = "[opts] [infile]"
+usage = '[opts] [infile]'
 nargs = 1
 nargs = 1
-iaction = "convert"
-oaction = "convert"
-bw_note = opt.opts.bw_note
-pw_note = opt.opts.pw_note
-
-if invoked_as == "gen":
-	desc = "Generate an {pnm} wallet from a random seed"
-	opt_filter = "ehdoJlLpPqrSvz"
-	usage = "[opts]"
-	oaction = "output"
+iaction = 'convert'
+oaction = 'convert'
+bw_note = opts.bw_note
+pw_note = opts.pw_note
+
+if invoked_as == 'gen':
+	desc = 'Generate an {pnm} wallet from a random seed'
+	opt_filter = 'ehdoJlLpPqrSvz'
+	usage = '[opts]'
+	oaction = 'output'
 	nargs = 0
 	nargs = 0
-elif invoked_as == "conv":
-	desc = "Convert an {pnm} wallet from one format to another"
+elif invoked_as == 'conv':
+	desc = 'Convert an {pnm} wallet from one format to another'
 	opt_filter = None
 	opt_filter = None
-elif invoked_as == "chk":
-	desc = "Check validity of an {pnm} wallet"
-	opt_filter = "ehiHOlpPqrvz"
-	iaction = "input"
-elif invoked_as == "passchg":
-	desc = "Change the password, hash preset or label of an {pnm} wallet"
-	opt_filter = "ehdiHkKOlLmpPqrSvz"
-	iaction = "input"
-	bw_note = ""
+elif invoked_as == 'chk':
+	desc = 'Check validity of an {pnm} wallet'
+	opt_filter = 'ehiHOlpPqrvz'
+	iaction = 'input'
+elif invoked_as == 'passchg':
+	desc = 'Change the password, hash preset or label of an {pnm} wallet'
+	opt_filter = 'ehdiHkKOlLmpPqrSvz'
+	iaction = 'input'
+	bw_note = ''
 else:
 else:
 	die(1,"'%s': unrecognized invocation" % bn)
 	die(1,"'%s': unrecognized invocation" % bn)
 
 
@@ -103,36 +102,36 @@ opts_data = {
 FMT CODES:
 FMT CODES:
   {f}
   {f}
 """.format(
 """.format(
-	f="\n  ".join(SeedSource.format_fmt_codes().splitlines()),
+	f='\n  '.join(SeedSource.format_fmt_codes().splitlines()),
 	pw_note=pw_note,
 	pw_note=pw_note,
-	bw_note=("","\n\n" + bw_note)[int(bool(bw_note))]
+	bw_note=('','\n\n' + bw_note)[bool(bw_note)]
 	)
 	)
 }
 }
 
 
-cmd_args = opt.opts.init(opts_data,opt_filter=opt_filter)
+cmd_args = opts.init(opts_data,opt_filter=opt_filter)
 
 
 if len(cmd_args) < nargs \
 if len(cmd_args) < nargs \
 		and not opt.hidden_incog_input_params and not opt.in_fmt:
 		and not opt.hidden_incog_input_params and not opt.in_fmt:
-	die(1,"An input file or input format must be specified")
+	die(1,'An input file or input format must be specified')
 elif len(cmd_args) > nargs \
 elif len(cmd_args) > nargs \
 		or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
 		or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
-	msg("No input files may be specified" if invoked_as == "gen"
-			else "Too many input files specified")
-	opt.opts.usage()
+	msg('No input files may be specified' if invoked_as == 'gen'
+			else 'Too many input files specified')
+	opts.usage()
 
 
 if cmd_args: check_infile(cmd_args[0])
 if cmd_args: check_infile(cmd_args[0])
 
 
-if not invoked_as == "chk": do_license_msg()
+if not invoked_as == 'chk': do_license_msg()
 
 
-if invoked_as in ("conv","passchg"): msg(green("Processing input wallet"))
+if invoked_as in ('conv','passchg'): msg(green('Processing input wallet'))
 
 
-ss_in = None if invoked_as == "gen" \
-			else SeedSource(*cmd_args,passchg=invoked_as=="passchg")
+ss_in = None if invoked_as == 'gen' \
+			else SeedSource(*cmd_args,passchg=invoked_as=='passchg')
 
 
-if invoked_as == "chk":
+if invoked_as == 'chk':
 	sys.exit()
 	sys.exit()
 
 
-if invoked_as in ("conv","passchg"): msg(green("Processing output wallet"))
+if invoked_as in ('conv','passchg'): msg(green('Processing output wallet'))
 
 
-ss_out = SeedSource(ss=ss_in,passchg=invoked_as=="passchg")
+ss_out = SeedSource(ss=ss_in,passchg=invoked_as=='passchg')
 ss_out.write_to_file()
 ss_out.write_to_file()

+ 1 - 1
mmgen/mn_electrum.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 1 - 1
mmgen/mn_tirosh.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 19 - 20
mmgen/obj.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
 obj.py:  The MMGenObject class and methods
 obj.py:  The MMGenObject class and methods
 """
 """
 import mmgen.globalvars as g
 import mmgen.globalvars as g
-from mmgen.util import mdie,mmsg
 
 
 lvl = 0
 lvl = 0
 
 
@@ -29,16 +28,16 @@ class MMGenObject(object):
 	# Pretty-print any object of type MMGenObject, recursing into sub-objects
 	# Pretty-print any object of type MMGenObject, recursing into sub-objects
 	def __str__(self):
 	def __str__(self):
 		global lvl
 		global lvl
-		indent = lvl * "    "
+		indent = lvl * '    '
 
 
 		def fix_linebreaks(v,fixed_indent=None):
 		def fix_linebreaks(v,fixed_indent=None):
-			if "\n" in v:
-				i = indent+"    " if fixed_indent == None else fixed_indent*" "
-				return "\n"+i + v.replace("\n","\n"+i)
+			if '\n' in v:
+				i = indent+'    ' if fixed_indent == None else fixed_indent*' '
+				return '\n'+i + v.replace('\n','\n'+i)
 			else: return repr(v)
 			else: return repr(v)
 
 
 		def conv(v,col_w):
 		def conv(v,col_w):
-			vret = ""
+			vret = ''
 			if type(v) == str:
 			if type(v) == str:
 				if not (set(list(v)) <= set(list(g.printable))):
 				if not (set(list(v)) <= set(list(g.printable))):
 					vret = repr(v)
 					vret = repr(v)
@@ -47,30 +46,30 @@ class MMGenObject(object):
 			elif type(v) == int or type(v) == long:
 			elif type(v) == int or type(v) == long:
 				vret = str(v)
 				vret = str(v)
 			elif type(v) == dict:
 			elif type(v) == dict:
-				sep = "\n{}{}".format(indent," "*4)
+				sep = '\n{}{}'.format(indent,' '*4)
 				cw = max(len(k) for k in v) + 2
 				cw = max(len(k) for k in v) + 2
-				t = sep.join(["{:<{w}}: {}".format(
+				t = sep.join(['{:<{w}}: {}'.format(
 					repr(k),
 					repr(k),
 	(fix_linebreaks(v[k],fixed_indent=0) if type(v[k]) == str else v[k]),
 	(fix_linebreaks(v[k],fixed_indent=0) if type(v[k]) == str else v[k]),
 					w=cw)
 					w=cw)
 				for k in sorted(v)])
 				for k in sorted(v)])
-				vret = "{" + sep + t + "\n" + indent + "}"
+				vret = '{' + sep + t + '\n' + indent + '}'
 			elif type(v) in (list,tuple):
 			elif type(v) in (list,tuple):
-				sep = "\n{}{}".format(indent," "*4)
-				t = " ".join([repr(e) for e in sorted(v)])
-				o,c = ("[","]") if type(v) == list else ("(",")")
-				vret = o + sep + t + "\n" + indent + c
+				sep = '\n{}{}'.format(indent,' '*4)
+				t = ' '.join([repr(e) for e in sorted(v)])
+				o,c = (('(',')'),('[',']'))[type(v)==list]
+				vret = o + sep + t + '\n' + indent + c
 			elif repr(v)[:14] == '<bound method ':
 			elif repr(v)[:14] == '<bound method ':
-				vret = " ".join(repr(v).split()[0:3]) + ">"
+				vret = ' '.join(repr(v).split()[0:3]) + '>'
 #				vret = repr(v)
 #				vret = repr(v)
 
 
 			return vret or type(v)
 			return vret or type(v)
 
 
 		out = []
 		out = []
-		def f(k): return k[:2] != "__"
+		def f(k): return k[:2] != '__'
 		keys = filter(f, dir(self))
 		keys = filter(f, dir(self))
 		col_w = max(len(k) for k in keys)
 		col_w = max(len(k) for k in keys)
-		fs = "{}%-{}s: %s".format(indent,col_w)
+		fs = '{}%-{}s: %s'.format(indent,col_w)
 
 
 		methods = [k for k in keys if repr(getattr(self,k))[:14] == '<bound method ']
 		methods = [k for k in keys if repr(getattr(self,k))[:14] == '<bound method ']
 
 
@@ -83,11 +82,11 @@ class MMGenObject(object):
 		for k in sorted(methods) + sorted(other) + sorted(objects):
 		for k in sorted(methods) + sorted(other) + sorted(objects):
 			val = getattr(self,k)
 			val = getattr(self,k)
 			if str(type(val))[:13] == "<class 'mmgen": # recurse into sub-objects
 			if str(type(val))[:13] == "<class 'mmgen": # recurse into sub-objects
-				out.append("\n%s%s (%s):" % (indent,k,type(val)))
+				out.append('\n%s%s (%s):' % (indent,k,type(val)))
 				lvl += 1
 				lvl += 1
-				out.append(str(getattr(self,k))+"\n")
+				out.append(str(getattr(self,k))+'\n')
 				lvl -= 1
 				lvl -= 1
 			else:
 			else:
 				out.append(fs % (k, conv(val,col_w)))
 				out.append(fs % (k, conv(val,col_w)))
 
 
-		return repr(self) + "\n    " + "\n    ".join(out)
+		return repr(self) + '\n    ' + '\n    '.join(out)

+ 65 - 71
mmgen/opts.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -21,10 +21,11 @@ opts.py:  MMGen-specific options processing after generic processing by share.Op
 """
 """
 import sys
 import sys
 
 
+class opt(object): pass
+
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 import mmgen.share.Opts
 import mmgen.share.Opts
-import opt
-from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die,is_mmgen_wallet_label
+from mmgen.util import *
 
 
 pw_note = """
 pw_note = """
 For passphrases all combinations of whitespace are equal and leading and
 For passphrases all combinations of whitespace are equal and leading and
@@ -41,7 +42,7 @@ seed, the same seed length and hash preset parameters must always be used.
 """.strip()
 """.strip()
 
 
 def usage():
 def usage():
-	Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
+	Msg('USAGE: %s %s' % (g.prog_name, usage_txt))
 	sys.exit(2)
 	sys.exit(2)
 
 
 def print_version_info():
 def print_version_info():
@@ -55,7 +56,7 @@ def die_on_incompatible_opts(incompat_list):
 	for group in incompat_list:
 	for group in incompat_list:
 		bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in group]
 		bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in group]
 		if len(bad) > 1:
 		if len(bad) > 1:
-			die(1,"Conflicting options: %s" % ", ".join([fmt_opt(b) for b in bad]))
+			die(1,'Conflicting options: %s' % ', '.join([fmt_opt(b) for b in bad]))
 
 
 def _typeconvert_from_dfl(key):
 def _typeconvert_from_dfl(key):
 
 
@@ -66,7 +67,7 @@ def _typeconvert_from_dfl(key):
 	gtype = type(gval)
 	gtype = type(gval)
 
 
 	try:
 	try:
-		opt.__dict__[key] = gtype(opt.__dict__[key])
+		setattr(opt,key,gtype(uval))
 	except:
 	except:
 		d = {
 		d = {
 			'int':   'an integer',
 			'int':   'an integer',
@@ -75,44 +76,45 @@ def _typeconvert_from_dfl(key):
 			'bool':  'a boolean value',
 			'bool':  'a boolean value',
 		}
 		}
 		die(1, "'%s': invalid parameter for '--%s' option (not %s)" % (
 		die(1, "'%s': invalid parameter for '--%s' option (not %s)" % (
-			opt.__dict__[key],
-			key.replace("_","-"),
+			uval,
+			key.replace('_','-'),
 			d[gtype.__name__]
 			d[gtype.__name__]
 		))
 		))
 
 
 	if g.debug:
 	if g.debug:
-		Msg("Opt overriden by user:\n    %-18s: %s" % (
-				key, ("%s -> %s" % (gval,uval))
+		Msg('Opt overriden by user:\n    %-18s: %s' % (
+				key, ('%s -> %s' % (gval,uval))
 			))
 			))
 
 
-def fmt_opt(o): return "--" + o.replace("_","-")
+def fmt_opt(o): return '--' + o.replace('_','-')
 
 
 def _show_hash_presets():
 def _show_hash_presets():
-	fs = "  {:<7} {:<6} {:<3}  {}"
-	msg("Available parameters for scrypt.hash():")
-	msg(fs.format("Preset","N","r","p"))
+	fs = '  {:<7} {:<6} {:<3}  {}'
+	msg('Available parameters for scrypt.hash():')
+	msg(fs.format('Preset','N','r','p'))
 	for i in sorted(g.hash_presets.keys()):
 	for i in sorted(g.hash_presets.keys()):
 		msg(fs.format("'%s'" % i, *g.hash_presets[i]))
 		msg(fs.format("'%s'" % i, *g.hash_presets[i]))
-	msg("N = memory usage (power of two), p = iterations (rounds)")
+	msg('N = memory usage (power of two), p = iterations (rounds)')
 
 
 def init(opts_data,add_opts=[],opt_filter=None):
 def init(opts_data,add_opts=[],opt_filter=None):
 
 
 	if len(sys.argv) == 2 and sys.argv[1] == '--version':
 	if len(sys.argv) == 2 and sys.argv[1] == '--version':
-		print_version_info(); sys.exit()
+		print_version_info()
+		sys.exit()
 
 
 	uopts,args,short_opts,long_opts,skipped_opts = \
 	uopts,args,short_opts,long_opts,skipped_opts = \
 		mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter)
 		mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter)
 
 
 	if g.debug:
 	if g.debug:
 		d = (
 		d = (
-			("Short opts",         short_opts),
-			("Long opts",          long_opts),
-			("Skipped opts",       skipped_opts),
-			("User-selected opts", uopts),
-			("Cmd args",           args),
+			('Short opts',         short_opts),
+			('Long opts',          long_opts),
+			('Skipped opts',       skipped_opts),
+			('User-selected opts', uopts),
+			('Cmd args',           args),
 		)
 		)
-		Msg("\n### BEGIN OPTS.PY ###")
-		for e in d: Msg("{:<20}: {}".format(*e))
+		Msg('\n=== opts.py debug ===')
+		for e in d: Msg('    {:<20}: {}'.format(*e))
 
 
 	# Save this for usage()
 	# Save this for usage()
 	global usage_txt
 	global usage_txt
@@ -123,61 +125,53 @@ def init(opts_data,add_opts=[],opt_filter=None):
 	for k in 'prog_name','desc','usage','options','notes':
 	for k in 'prog_name','desc','usage','options','notes':
 		if k in opts_data: del opts_data[k]
 		if k in opts_data: del opts_data[k]
 
 
-	# Remove all unneeded attributes from opt, our special global namespace
-	for k in dir(opt):
-		if k[:2] == "__": del opt.__dict__[k]
-
 	# Transfer uopts into opt, setting required opts to None if not set by user
 	# Transfer uopts into opt, setting required opts to None if not set by user
-	for o in [s.rstrip("=") for s in long_opts] + \
+	for o in [s.rstrip('=') for s in long_opts] + \
 			g.required_opts + add_opts + skipped_opts:
 			g.required_opts + add_opts + skipped_opts:
-		opt.__dict__[o] = uopts[o] if o in uopts else None
+		setattr(opt,o,uopts[o] if o in uopts else None)
 
 
 	# A special case - do this here, before opt gets set from g.dfl_vars
 	# A special case - do this here, before opt gets set from g.dfl_vars
 	if opt.usr_randchars: g.use_urandchars = True
 	if opt.usr_randchars: g.use_urandchars = True
 
 
 	# If user opt is set, convert its type based on value in mmgen.globalvars
 	# If user opt is set, convert its type based on value in mmgen.globalvars
 	# If unset, set it to default value in mmgen.globalvars (g):
 	# If unset, set it to default value in mmgen.globalvars (g):
-	opt.__dict__['set_by_user'] = []
+	setattr(opt,'set_by_user',[])
 	for k in g.dfl_vars:
 	for k in g.dfl_vars:
 		if k in opt.__dict__ and opt.__dict__[k] != None:
 		if k in opt.__dict__ and opt.__dict__[k] != None:
 			_typeconvert_from_dfl(k)
 			_typeconvert_from_dfl(k)
 			opt.set_by_user.append(k)
 			opt.set_by_user.append(k)
 		else:
 		else:
-			opt.__dict__[k] = g.__dict__[k]
+			setattr(opt,k,g.__dict__[k])
 
 
 	# Check user-set opts without modifying them
 	# Check user-set opts without modifying them
-	if not check_opts(uopts): sys.exit(1)
+	if not check_opts(uopts):
+		sys.exit(1)
 
 
 	if opt.show_hash_presets:
 	if opt.show_hash_presets:
-		_show_hash_presets(); sys.exit(0)
+		_show_hash_presets()
+		sys.exit()
 
 
 	if opt.debug: opt.verbose = True
 	if opt.debug: opt.verbose = True
 
 
 	if g.debug:
 	if g.debug:
-		Msg("Opts after processing:")
-		for k in opt.__dict__:
-			v = opt.__dict__[k]
-			if v != None and k != "opts":
-				Msg("    %-18s: %-6s [%s]" % (k,v,type(v).__name__))
-		Msg("### END OPTS.PY ###\n")
+		a = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) != None]
+		b = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) == None]
+		Msg('    Opts after processing:')
+		for k in a:
+			v = getattr(opt,k)
+			Msg('        %-18s: %-6s [%s]' % (k,v,type(v).__name__))
+		Msg("    Opts set to 'None':")
+		Msg('        %s\n' % '\n        '.join(b))
 
 
 	die_on_incompatible_opts(g.incompatible_opts)
 	die_on_incompatible_opts(g.incompatible_opts)
 
 
 	return args
 	return args
 
 
-# save for debugging
-def show_all_opts():
-	msg("Processed options:")
-	d = opt.__dict__
-	for k in [o for o in d if o != "opts"]:
-		tstr = type(d[k]) if d[k] not in (None,False,True) else ""
-		msg("%-20s: %-8s %s" % (k, d[k], tstr))
 
 
 def check_opts(usr_opts):       # Returns false if any check fails
 def check_opts(usr_opts):       # Returns false if any check fails
 
 
 	def opt_splits(val,sep,n,desc):
 	def opt_splits(val,sep,n,desc):
-		sepword = "comma" if sep == "," else (
-					"colon" if sep == ":" else ("'"+sep+"'"))
+		sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'%s'" % sep
 		try: l = val.split(sep)
 		try: l = val.split(sep)
 		except:
 		except:
 			msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword))
 			msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword))
@@ -189,10 +183,10 @@ def check_opts(usr_opts):       # Returns false if any check fails
 					(val,desc,n,sepword))
 					(val,desc,n,sepword))
 			return False
 			return False
 
 
-	def opt_compares(val,op,target,desc,what=""):
-		if what: what += " "
-		if not eval("%s %s %s" % (val, op, target)):
-			msg("%s: invalid %s (%snot %s %s)" % (val,desc,what,op,target))
+	def opt_compares(val,op,target,desc,what=''):
+		if what: what += ' '
+		if not eval('%s %s %s' % (val, op, target)):
+			msg('%s: invalid %s (%snot %s %s)' % (val,desc,what,op,target))
 			return False
 			return False
 		return True
 		return True
 
 
@@ -205,8 +199,8 @@ def check_opts(usr_opts):       # Returns false if any check fails
 
 
 	def opt_is_in_list(val,lst,desc):
 	def opt_is_in_list(val,lst,desc):
 		if val not in lst:
 		if val not in lst:
-			q,sep = ("'","','") if type(lst[0]) == str else ("",",")
-			msg("{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}".format(
+			q,sep = (('',','),("'","','"))[type(lst[0]) == str]
+			msg('{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format(
 					v=val,w=desc,q=q,
 					v=val,w=desc,q=q,
 					o=sep.join([str(i) for i in sorted(lst)])
 					o=sep.join([str(i) for i in sorted(lst)])
 				))
 				))
@@ -218,8 +212,8 @@ def check_opts(usr_opts):       # Returns false if any check fails
 				% (val,desc,fmt_opt(key)))
 				% (val,desc,fmt_opt(key)))
 		return False
 		return False
 
 
-	def opt_display(key,val='',beg="For selected",end=":\n"):
-		s = "%s=%s" % (fmt_opt(key),val) if val else fmt_opt(key)
+	def opt_display(key,val='',beg='For selected',end=':\n'):
+		s = '%s=%s' % (fmt_opt(key),val) if val else fmt_opt(key)
 		msg_r("%s option '%s'%s" % (beg,s,end))
 		msg_r("%s option '%s'%s" % (beg,s,end))
 
 
 	global opt
 	global opt
@@ -245,23 +239,23 @@ def check_opts(usr_opts):       # Returns false if any check fails
 			from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden
 			from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden
 			sstype = SeedSource.fmt_code_to_sstype(val)
 			sstype = SeedSource.fmt_code_to_sstype(val)
 			if not sstype:
 			if not sstype:
-				return opt_unrecognized(key,val,"format code")
+				return opt_unrecognized(key,val,'format code')
 			if key == 'out_fmt':
 			if key == 'out_fmt':
 				p = 'hidden_incog_output_params'
 				p = 'hidden_incog_output_params'
 				if sstype == IncogWalletHidden and not getattr(opt,p):
 				if sstype == IncogWalletHidden and not getattr(opt,p):
-						die(1,"Hidden incog format output requested. You must supply"
+						die(1,'Hidden incog format output requested. You must supply'
 						+ " a file and offset with the '%s' option" % fmt_opt(p))
 						+ " a file and offset with the '%s' option" % fmt_opt(p))
 				if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
 				if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
-					opt_display(key,val,beg="Selected",end=" ")
-					opt_display('old_incog_fmt',beg="conflicts with",end=":\n")
-					die(1,"Export to old incog wallet format unsupported")
+					opt_display(key,val,beg='Selected',end=' ')
+					opt_display('old_incog_fmt',beg='conflicts with',end=':\n')
+					die(1,'Export to old incog wallet format unsupported')
 				elif issubclass(sstype,Brainwallet):
 				elif issubclass(sstype,Brainwallet):
-					die(1,"Output to brainwallet format unsupported")
+					die(1,'Output to brainwallet format unsupported')
 		elif key in ('hidden_incog_input_params','hidden_incog_output_params'):
 		elif key in ('hidden_incog_input_params','hidden_incog_output_params'):
-			a = val.split(",")
+			a = val.split(',')
 			if len(a) != 2:
 			if len(a) != 2:
 				opt_display(key,val)
 				opt_display(key,val)
-				msg("Option requires two comma-separated arguments")
+				msg('Option requires two comma-separated arguments')
 				return False
 				return False
 			if not opt_is_int(a[1],desc): return False
 			if not opt_is_int(a[1],desc): return False
 			if key == 'hidden_incog_input_params':
 			if key == 'hidden_incog_input_params':
@@ -280,7 +274,7 @@ def check_opts(usr_opts):       # Returns false if any check fails
 				from mmgen.seed import IncogWalletHidden
 				from mmgen.seed import IncogWalletHidden
 				if val2 and val2 not in IncogWalletHidden.fmt_codes:
 				if val2 and val2 not in IncogWalletHidden.fmt_codes:
 					die(1,
 					die(1,
-						"Option conflict:\n  %s, with\n  %s=%s" % (
+						'Option conflict:\n  %s, with\n  %s=%s' % (
 						fmt_opt(key),fmt_opt(key2),val2
 						fmt_opt(key),fmt_opt(key2),val2
 					))
 					))
 		elif key == 'seed_len':
 		elif key == 'seed_len':
@@ -289,21 +283,21 @@ def check_opts(usr_opts):       # Returns false if any check fails
 		elif key == 'hash_preset':
 		elif key == 'hash_preset':
 			if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False
 			if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False
 		elif key == 'brain_params':
 		elif key == 'brain_params':
-			a = val.split(",")
+			a = val.split(',')
 			if len(a) != 2:
 			if len(a) != 2:
 				opt_display(key,val)
 				opt_display(key,val)
-				msg("Option requires two comma-separated arguments")
+				msg('Option requires two comma-separated arguments')
 				return False
 				return False
-			d = "seed length " + desc
+			d = 'seed length ' + desc
 			if not opt_is_int(a[0],d): return False
 			if not opt_is_int(a[0],d): return False
 			if not opt_is_in_list(int(a[0]),g.seed_lens,d): return False
 			if not opt_is_in_list(int(a[0]),g.seed_lens,d): return False
-			d = "hash preset " + desc
+			d = 'hash preset ' + desc
 			if not opt_is_in_list(a[1],g.hash_presets.keys(),d): return False
 			if not opt_is_in_list(a[1],g.hash_presets.keys(),d): return False
 		elif key == 'usr_randchars':
 		elif key == 'usr_randchars':
 			if val == 0: continue
 			if val == 0: continue
 			if not opt_is_int(val,desc): return False
 			if not opt_is_int(val,desc): return False
-			if not opt_compares(val,">=",g.min_urandchars,desc): return False
-			if not opt_compares(val,"<=",g.max_urandchars,desc): return False
+			if not opt_compares(val,'>=',g.min_urandchars,desc): return False
+			if not opt_compares(val,'<=',g.max_urandchars,desc): return False
 		else:
 		else:
 			if g.debug: Msg("check_opts(): No test for opt '%s'" % key)
 			if g.debug: Msg("check_opts(): No test for opt '%s'" % key)
 
 

+ 128 - 0
mmgen/rpc.py

@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+#
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+rpc.py:  Bitcoin RPC library for the MMGen suite
+"""
+
+import httplib,base64,json,decimal
+
+from mmgen.common import *
+
+class BitcoinRPCConnection(object):
+
+	def __init__(
+				self,
+				host='localhost',port=8332,
+				user=None,passwd=None,auth_cookie=None,
+			):
+
+		if auth_cookie:
+			self.auth_str = auth_cookie
+		elif user and passwd:
+			self.auth_str = '{}:{}'.format(user,passwd)
+		else:
+			msg('Error: no Bitcoin RPC authentication method found')
+			if passwd: die(1,"'rpcuser' entry missing in bitcoin.conf")
+			elif user: die(1,"'rpcpassword' entry missing in bitcoin.conf")
+			else:
+				m1 = 'Either provide rpcuser/rpcpassword in bitcoin.conf'
+				m2 = '(or, alternatively, copy the authentication cookie to Bitcoin data dir'
+				m3 = 'if {} and Bitcoin are running as different users)'.format(g.proj_name)
+				die(1,'\n'.join((m1,m2,m3)))
+
+		self.host = host
+		self.port = port
+
+	# Normal mode: call with arg list unrolled, exactly as with 'bitcoin-cli'
+	# Batch mode:  call with list of arg lists as first argument
+	# kwargs are for local use and are not passed to server
+	def request(self,cmd,*args,**kwargs):
+
+		cf = { 'timeout': g.http_timeout, 'batch': False }
+
+		for k in cf:
+			if k in kwargs and kwargs[k]: cf[k] = kwargs[k]
+
+		c = httplib.HTTPConnection(self.host, self.port, False, cf['timeout'])
+
+		if cf['batch']:
+			p = [{'method':cmd,'params':r,'id':n} for n,r in enumerate(args[0],1)]
+		else:
+			p = {'method':cmd,'params':args,'id':1}
+
+		dmsg('=== rpc.py debug ===')
+		dmsg('    RPC POST data ==> %s\n' % p)
+
+		try:
+			c.request('POST', '/', json.dumps(p), {
+				'Host': self.host,
+				'Authorization': 'Basic ' + base64.b64encode(self.auth_str)
+			})
+		except Exception as e:
+			die(2,'%s\nUnable to connect to bitcoind' % e)
+
+		r = c.getresponse() # returns HTTPResponse instance
+
+		if r.status == 401:
+			m1 = 'RPC authentication error'
+			m2 = 'Check that rpcuser/rpcpassword in Bitcoin config file are correct'
+			m3 = '(or, alternatively, copy the authentication cookie to Bitcoin data dir'
+			m4 = 'if {} and Bitcoin are running as different users)'.format(g.proj_name)
+			die(1,'\n'.join((m1,m2,m3,m4)))
+		elif r.status != 200:
+			die(1,'RPC error: %s %s\n%s' % (r.status, r.reason, r.read()))
+
+		r2 = r.read()
+
+		dmsg('    RPC REPLY data ==> %s\n' % r2)
+
+		if not r2:
+			die(2,'Error: empty reply')
+
+		r3 = json.loads(r2.decode('utf8'), parse_float=decimal.Decimal)
+		ret = []
+
+		for resp in r3 if cf['batch'] else [r3]:
+			if 'error' in resp and resp['error'] != None:
+				die(1,'Bitcoind returned an error: %s' % resp['error'])
+			elif 'result' not in resp:
+				die(1, 'Missing JSON-RPC result\n' + repr(resps))
+			else:
+				ret.append(resp['result'])
+
+		return ret if cf['batch'] else ret[0]
+
+
+	rpcmethods = (
+		'getinfo',
+		'getbalance',
+		'getaddressesbyaccount',
+		'listunspent',
+		'listaccounts',
+		'importaddress',
+		'decoderawtransaction',
+		'createrawtransaction',
+		'signrawtransaction',
+		'sendrawtransaction',
+		'walletpassphrase',
+		'walletlock',
+	)
+
+	for name in rpcmethods:
+		exec "def {n}(self,*a,**k):return self.request('{n}',*a,**k)\n".format(n=name)

+ 0 - 53
mmgen/rpc/__init__.py

@@ -1,53 +0,0 @@
-# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-"""
-bitcoin-python - Easy-to-use Bitcoin API client
-"""
-
-
-def connect_to_local(filename=None):
-	"""
-    Connect to default bitcoin instance owned by this user, on this machine.
-
-    Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
-
-    Arguments:
-
-        - `filename`: Path to a configuration file in a non-standard location (optional)
-	"""
-	from mmgen.rpc.connection import BitcoinConnection
-	from mmgen.rpc.config import read_default_config
-
-	cfg = read_default_config(filename)
-	port = int(cfg.get('rpcport', '18332' if cfg.get('testnet') else '8332'))
-	rcpuser = cfg.get('rpcuser', '')
-
-	return BitcoinConnection(rcpuser, cfg['rpcpassword'], 'localhost', port)
-
-
-def connect_to_remote(user,password,host='localhost',port=8332,use_https=False):
-	"""
-    Connect to remote or alternative local bitcoin client instance.
-
-    Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
-	"""
-	from mmgen.rpc.connection import BitcoinConnection
-
-	return BitcoinConnection(user, password, host, port, use_https)

+ 0 - 75
mmgen/rpc/config.py

@@ -1,75 +0,0 @@
-# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-"""
-Utilities for reading bitcoin configuration files.
-"""
-
-
-def read_config_file(filename):
-	"""
-    Read a simple ``'='``-delimited config file.
-    Raises :const:`IOError` if unable to open file, or :const:`ValueError`
-    if an parse error occurs.
-	"""
-	f = open(filename)
-	try:
-		cfg = {}
-		for line in f:
-			line = line.strip()
-			if line and not line.startswith("#"):
-				try:
-					(key, value) = line.split('=', 1)
-					cfg[key] = value
-				except ValueError:
-					pass  # Happens when line has no '=', ignore
-	finally:
-		f.close()
-	return cfg
-
-
-def read_default_config(filename=None):
-	"""
-    Read bitcoin default configuration from the current user's home directory.
-
-    Arguments:
-
-    - `filename`: Path to a configuration file in a non-standard location (optional)
-	"""
-	if filename is None:
-		import os
-		import platform
-		home = os.getenv("HOME")
-		if not home:
-			raise IOError("Home directory not defined, don't know where to look for config file")
-
-		if platform.system() == "Darwin":
-			location = 'Library/Application Support/Bitcoin/bitcoin.conf'
-		else:
-			location = '.bitcoin/bitcoin.conf'
-		filename = os.path.join(home, location)
-
-	elif filename.startswith("~"):
-		import os
-		filename = os.path.expanduser(filename)
-
-	try:
-		return read_config_file(filename)
-	except (IOError, ValueError):
-		pass  # Cannot read config file, ignore

+ 0 - 766
mmgen/rpc/connection.py

@@ -1,766 +0,0 @@
-# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
-# Added methods for sendrawtransaction(), importaddress()
-#
-# Previous copyright from bitcoin-python/connection.py:
-# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-"""
-Connect to Bitcoin server via JSON-RPC.
-"""
-from mmgen.rpc.proxy import JSONRPCException, AuthServiceProxy
-from mmgen.rpc.exceptions import _wrap_exception, WalletPassphraseIncorrect, WalletAlreadyUnlocked
-from mmgen.rpc.data import (ServerInfo, AccountInfo, AddressInfo, TransactionInfo,
-							AddressValidation, WorkItem, MiningInfo)
-
-
-class BitcoinConnection(object):
-	"""
-    A BitcoinConnection object defines a connection to a bitcoin server.
-    It is a thin wrapper around a JSON-RPC API connection.
-
-    Up-to-date for SVN revision 198.
-
-    Arguments to constructor:
-
-    - *user* -- Authenticate as user.
-    - *password* -- Authentication password.
-    - *host* -- Bitcoin JSON-RPC host.
-    - *port* -- Bitcoin JSON-RPC port.
-	"""
-	def __init__(self, user, password, host='localhost', port=8332, use_https=False):
-		"""
-        Create a new bitcoin server connection.
-		"""
-		url = 'http{s}://{user}:{password}@{host}:{port}/'.format(
-			s='s' if use_https else '',
-			user=user, password=password, host=host, port=port)
-		self.url = url
-		try:
-			self.proxy = AuthServiceProxy(url)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-# importaddress <address> [label] [rescan=true]
-	def importaddress(self,address,label=None,rescan=True):
-		try:
-#			return self.proxy.badmethod(address,label) # DEBUG
-			return self.proxy.importaddress(address,label,rescan)
-		except JSONRPCException as e:
-			if e.error['message'] == "Method not found":
-				from mmgen.util import msg
-				msg("""
-*******************************************************************************
-*******************************************************************************
-ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
-*******************************************************************************
-*******************************************************************************
-""")
-			raise _wrap_exception(e.error)
-
-# sendrawtransaction <hex string> [allowhighfees=false]
-	def sendrawtransaction(self,tx):
-		try:
-			return self.proxy.sendrawtransaction(tx)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def stop(self):
-		"""
-        Stop bitcoin server.
-		"""
-		try:
-			self.proxy.stop()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getblock(self, hash):
-		"""
-        Returns information about the given block hash.
-		"""
-		try:
-			return self.proxy.getblock(hash)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getblockcount(self):
-		"""
-        Returns the number of blocks in the longest block chain.
-		"""
-		try:
-			return self.proxy.getblockcount()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getblockhash(self, index):
-		"""
-        Returns hash of block in best-block-chain at index.
-
-        :param index: index ob the block
-
-		"""
-		try:
-			return self.proxy.getblockhash(index)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getblocknumber(self):
-		"""
-        Returns the block number of the latest block in the longest block chain.
-        Deprecated. Use getblockcount instead.
-		"""
-		return self.getblockcount()
-
-	def getconnectioncount(self):
-		"""
-        Returns the number of connections to other nodes.
-		"""
-		try:
-			return self.proxy.getconnectioncount()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getdifficulty(self):
-		"""
-        Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
-		"""
-		try:
-			return self.proxy.getdifficulty()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getgenerate(self):
-		"""
-        Returns :const:`True` or :const:`False`, depending on whether
-        generation is enabled.
-		"""
-		try:
-			return self.proxy.getgenerate()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def setgenerate(self, generate, genproclimit=None):
-		"""
-        Enable or disable generation (mining) of coins.
-
-        Arguments:
-
-        - *generate* -- is :const:`True` or :const:`False` to turn generation
-        on or off.
-        - *genproclimit* -- Number of processors that are used for generation,
-        -1 is unlimited.
-
-		"""
-		try:
-			if genproclimit is None:
-				return self.proxy.setgenerate(generate)
-			else:
-				return self.proxy.setgenerate(generate, genproclimit)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def gethashespersec(self):
-		"""
-        Returns a recent hashes per second performance measurement while generating.
-		"""
-		try:
-			return self.proxy.gethashespersec()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getinfo(self):
-		"""
-        Returns an :class:`~mmgen.rpc.data.ServerInfo` object containing
-        various state info.
-		"""
-		try:
-			return ServerInfo(**self.proxy.getinfo())
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getmininginfo(self):
-		"""
-        Returns an :class:`~mmgen.rpc.data.MiningInfo` object containing various
-        mining state info.
-		"""
-		try:
-			return MiningInfo(**self.proxy.getmininginfo())
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getnewaddress(self, account=None):
-		"""
-        Returns a new bitcoin address for receiving payments.
-
-        Arguments:
-
-        - *account* -- If account is specified (recommended), it is added to the
-        address book so that payments received with the address will be
-        credited to it.
-
-		"""
-		try:
-			if account is None:
-				return self.proxy.getnewaddress()
-			else:
-				return self.proxy.getnewaddress(account)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getaccountaddress(self, account):
-		"""
-        Returns the current bitcoin address for receiving payments to an account.
-
-        Arguments:
-
-        - *account* -- Account for which the address should be returned.
-
-		"""
-		try:
-			return self.proxy.getaccountaddress(account)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def setaccount(self, bitcoinaddress, account):
-		"""
-        Sets the account associated with the given address.
-
-        Arguments:
-
-        - *bitcoinaddress* -- Bitcoin address to associate.
-        - *account* -- Account to associate the address to.
-
-		"""
-		try:
-			return self.proxy.setaccount(bitcoinaddress, account)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getaccount(self, bitcoinaddress):
-		"""
-        Returns the account associated with the given address.
-
-        Arguments:
-
-        - *bitcoinaddress* -- Bitcoin address to get account for.
-		"""
-		try:
-			return self.proxy.getaccount(bitcoinaddress)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getaddressesbyaccount(self, account):
-		"""
-        Returns the list of addresses for the given account.
-
-        Arguments:
-
-        - *account* -- Account to get list of addresses for.
-		"""
-		try:
-			return self.proxy.getaddressesbyaccount(account)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def sendtoaddress(self, bitcoinaddress, amount, comment=None, comment_to=None):
-		"""
-        Sends *amount* from the server's available balance to *bitcoinaddress*.
-
-        Arguments:
-
-        - *bitcoinaddress* -- Bitcoin address to send to.
-        - *amount* -- Amount to send (float, rounded to the nearest 0.01).
-        - *minconf* -- Minimum number of confirmations required for transferred
-        balance.
-        - *comment* -- Comment for transaction.
-        - *comment_to* -- Comment for to-address.
-
-		"""
-		try:
-			if comment is None:
-				return self.proxy.sendtoaddress(bitcoinaddress, amount)
-			elif comment_to is None:
-				return self.proxy.sendtoaddress(bitcoinaddress, amount, comment)
-			else:
-				return self.proxy.sendtoaddress(bitcoinaddress, amount, comment, comment_to)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getreceivedbyaddress(self, bitcoinaddress, minconf=1):
-		"""
-        Returns the total amount received by a bitcoin address in transactions
-        with at least a certain number of confirmations.
-
-        Arguments:
-
-        - *bitcoinaddress* -- Address to query for total amount.
-
-        - *minconf* -- Number of confirmations to require, defaults to 1.
-		"""
-		try:
-			return self.proxy.getreceivedbyaddress(bitcoinaddress, minconf)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getreceivedbyaccount(self, account, minconf=1):
-		"""
-        Returns the total amount received by addresses with an account in
-        transactions with at least a certain number of confirmations.
-
-        Arguments:
-
-        - *account* -- Account to query for total amount.
-        - *minconf* -- Number of confirmations to require, defaults to 1.
-
-		"""
-		try:
-			return self.proxy.getreceivedbyaccount(account, minconf)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def gettransaction(self, txid):
-		"""
-        Get detailed information about transaction
-
-        Arguments:
-
-        - *txid* -- Transactiond id for which the info should be returned
-
-		"""
-		try:
-			return TransactionInfo(**self.proxy.gettransaction(txid))
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getrawtransaction(self, txid, verbose=True):
-		"""
-        Get transaction raw info
-
-        Arguments:
-
-        - *txid* -- Transactiond id for which the info should be returned.
-        - *verbose* -- If False, return only the "hex" of the transaction.
-
-		"""
-		try:
-			if verbose:
-				return TransactionInfo(**self.proxy.getrawtransaction(txid, 1))
-			return self.proxy.getrawtransaction(txid, 0)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def createrawtransaction(self, inputs, outputs):
-		"""
-        Creates a raw transaction spending given inputs
-        (a list of dictionaries, each containing a transaction id and an output
-        number), sending to given address(es).
-
-        Returns hex-encoded raw transaction.
-
-        Example usage:
-        >>> conn.createrawtransaction(
-               [{"txid": "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c",
-               "vout": 0}],
-               {"mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT":50})
-
-
-        Arguments:
-
-        - *inputs* -- A list of {"txid": txid, "vout": n} dictionaries.
-        - *outputs* -- A dictionary mapping (public) addresses to the amount
-                    they are to be paid.
-		"""
-		try:
-			return self.proxy.createrawtransaction(inputs, outputs)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def signrawtransaction(self, hexstring, previous_transactions=None, private_keys=None):
-		"""
-        Sign inputs for raw transaction (serialized, hex-encoded).
-
-        Returns a dictionary with the keys:
-            "hex": raw transaction with signature(s) (hex-encoded string)
-            "complete": 1 if transaction has a complete set of signature(s), 0 if not
-
-        Arguments:
-
-        - *hexstring* -- A hex string of the transaction to sign.
-        - *previous_transactions* -- A (possibly empty) list of dictionaries of
-        the form:
-            {"txid": txid, "vout": n, "scriptPubKey": hex, "redeemScript": hex},
-            representing previous transaction outputs that this transaction depends
-            on but may not yet be in the block chain.
-        - *private_keys* -- A (possibly empty) list of base58-encoded private
-        keys that, if given, will be the only keys used to sign the transaction.
-		"""
-		try:
-			return dict(self.proxy.signrawtransaction(hexstring,
-						previous_transactions, private_keys))
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def decoderawtransaction(self, hexstring):
-		"""
-        Produces a human-readable JSON object for a raw transaction.
-
-        Arguments:
-
-        - *hexstring* -- A hex string of the transaction to be decoded.
-		"""
-		try:
-			return dict(self.proxy.decoderawtransaction(hexstring))
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def listsinceblock(self, block_hash):
-		try:
-			res = self.proxy.listsinceblock(block_hash)
-			res['transactions'] = [TransactionInfo(**x) for x in res['transactions']]
-			return res
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def listreceivedbyaddress(self, minconf=1, includeempty=False):
-		"""
-        Returns a list of addresses.
-
-        Each address is represented with a
-        :class:`~mmgen.rpc.data.AddressInfo` object.
-
-        Arguments:
-
-        - *minconf* -- Minimum number of confirmations before payments are included.
-        - *includeempty* -- Whether to include addresses that haven't received
-        any payments.
-
-		"""
-		try:
-			return [AddressInfo(**x) for x in
-					self.proxy.listreceivedbyaddress(minconf, includeempty)]
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def listaccounts(self, minconf=1, includeWatchonly=False, as_dict=False):
-		"""
-        Returns a list of account names.
-
-        Arguments:
-
-        - *minconf* -- Minimum number of confirmations before payments are included.
-        - *as_dict* -- Returns a dictionary of account names, with their balance as values.
-		"""
-		try:
-			if as_dict:
-				return dict(self.proxy.listaccounts(minconf,includeWatchonly))
-			else:
-				return self.proxy.listaccounts(minconf,includeWatchonly).keys()
-		except JSONRPCException as e:
-			from mmgen.util import msg
-			msg("""
-*******************************************************************************
-*******************************************************************************
-ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
-*******************************************************************************
-*******************************************************************************
-""")
-			raise _wrap_exception(e.error)
-
-	def listreceivedbyaccount(self, minconf=1, includeempty=False):
-		"""
-        Returns a list of accounts.
-
-        Each account is represented with a :class:`~mmgen.rpc.data.AccountInfo` object.
-
-        Arguments:
-
-        - *minconf* -- Minimum number of confirmations before payments are included.
-
-        - *includeempty* -- Whether to include addresses that haven't received any payments.
-		"""
-		try:
-			return [AccountInfo(**x) for x in
-					self.proxy.listreceivedbyaccount(minconf, includeempty)]
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def listtransactions(self, account=None, count=10, from_=0, address=None):
-		"""
-        Returns a list of the last transactions for an account.
-
-        Each transaction is represented with a :class:`~mmgen.rpc.data.TransactionInfo` object.
-
-        Arguments:
-
-        - *account* -- Account to list transactions from. Return transactions from
-                    all accounts if None.
-        - *count* -- Number of transactions to return.
-        - *from_* -- Skip the first <from_> transactions.
-        - *address* -- Receive address to consider
-		"""
-		accounts = [account] if account is not None else self.listaccounts(as_dict=True).iterkeys()
-		try:
-			return [TransactionInfo(**tx) for acc in accounts for
-					tx in self.proxy.listtransactions(acc, count, from_) if
-					address is None or tx["address"] == address]
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def backupwallet(self, destination):
-		"""
-        Safely copies ``wallet.dat`` to *destination*, which can be a directory or a path
-        with filename.
-
-        Arguments:
-        - *destination* -- directory or path with filename to backup wallet to.
-
-		"""
-		try:
-			return self.proxy.backupwallet(destination)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def validateaddress(self, validateaddress):
-		"""
-        Validate a bitcoin address and return information for it.
-
-        The information is represented by a :class:`~mmgen.rpc.data.AddressValidation` object.
-
-        Arguments: -- Address to validate.
-
-
-        - *validateaddress*
-		"""
-		try:
-			return AddressValidation(**self.proxy.validateaddress(validateaddress))
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getbalance(self, account=None, minconf=None):
-		"""
-        Get the current balance, either for an account or the total server balance.
-
-        Arguments:
-        - *account* -- If this parameter is specified, returns the balance in the account.
-        - *minconf* -- Minimum number of confirmations required for transferred balance.
-
-		"""
-		args = []
-		if account:
-			args.append(account)
-			if minconf is not None:
-				args.append(minconf)
-		try:
-			return self.proxy.getbalance(*args)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def move(self, fromaccount, toaccount, amount, minconf=1, comment=None):
-		"""
-        Move from one account in your wallet to another.
-
-        Arguments:
-
-        - *fromaccount* -- Source account name.
-        - *toaccount* -- Destination account name.
-        - *amount* -- Amount to transfer.
-        - *minconf* -- Minimum number of confirmations required for transferred balance.
-        - *comment* -- Comment to add to transaction log.
-
-		"""
-		try:
-			if comment is None:
-				return self.proxy.move(fromaccount, toaccount, amount, minconf)
-			else:
-				return self.proxy.move(fromaccount, toaccount, amount, minconf, comment)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def sendfrom(self, fromaccount, tobitcoinaddress, amount, minconf=1, comment=None,
-				comment_to=None):
-		"""
-        Sends amount from account's balance to bitcoinaddress. This method will fail
-        if there is less than amount bitcoins with minconf confirmations in the account's
-        balance (unless account is the empty-string-named default account; it
-        behaves like the sendtoaddress method). Returns transaction ID on success.
-
-        Arguments:
-
-        - *fromaccount* -- Account to send from.
-        - *tobitcoinaddress* -- Bitcoin address to send to.
-        - *amount* -- Amount to send (float, rounded to the nearest 0.01).
-        - *minconf* -- Minimum number of confirmations required for transferred balance.
-        - *comment* -- Comment for transaction.
-        - *comment_to* -- Comment for to-address.
-
-		"""
-		try:
-			if comment is None:
-				return self.proxy.sendfrom(fromaccount, tobitcoinaddress, amount, minconf)
-			elif comment_to is None:
-				return self.proxy.sendfrom(fromaccount, tobitcoinaddress, amount, minconf, comment)
-			else:
-				return self.proxy.sendfrom(fromaccount, tobitcoinaddress, amount, minconf, comment, comment_to)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def sendmany(self, fromaccount, todict, minconf=1, comment=None):
-		"""
-        Sends specified amounts from account's balance to bitcoinaddresses.
-        This method will fail if there is less than total amount bitcoins with
-        minconf confirmations in the account's balance (unless account is the
-        empty-string-named default account; Returns transaction ID on
-        success.
-
-        Arguments:
-
-        - *fromaccount* -- Account to send from.
-        - *todict* -- Dictionary with Bitcoin addresses as keys and amounts as
-        values.
-        - *minconf* -- Minimum number of confirmations required for transferred
-        balance.
-        - *comment* -- Comment for transaction.
-
-		"""
-		try:
-			if comment is None:
-				return self.proxy.sendmany(fromaccount, todict, minconf)
-			else:
-				return self.proxy.sendmany(fromaccount, todict, minconf, comment)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def verifymessage(self, bitcoinaddress, signature, message):
-		"""
-        Verifies a signature given the bitcoinaddress used to sign,
-        the signature itself, and the message that was signed.
-        Returns :const:`True` if the signature is valid, and :const:`False` if it is invalid.
-
-        Arguments:
-
-        - *bitcoinaddress* -- the bitcoinaddress used to sign the message
-        - *signature* -- the signature to be verified
-        - *message* -- the message that was originally signed
-
-		"""
-		try:
-			return self.proxy.verifymessage(bitcoinaddress, signature, message)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def getwork(self, data=None):
-		"""
-        Get work for remote mining, or submit result.
-        If data is specified, the server tries to solve the block
-        using the provided data and returns :const:`True` if it was successful.
-        If not, the function returns formatted hash data (:class:`~mmgen.rpc.data.WorkItem`)
-        to work on.
-
-        Arguments:
-
-        - *data* -- Result from remote mining.
-
-		"""
-		try:
-			if data is None:
-				# Only if no data provided, it returns a WorkItem
-				return WorkItem(**self.proxy.getwork())
-			else:
-				return self.proxy.getwork(data)
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def listunspent(self, minconf=1, maxconf=999999):
-		"""
-        Returns a list of unspent transaction inputs in the wallet.
-
-        Arguments:
-
-        - *minconf* -- Minimum number of confirmations required to be listed.
-
-        - *maxconf* -- Maximal number of confirmations allowed to be listed.
-
-
-		"""
-		try:
-			return [TransactionInfo(**tx) for tx in
-					self.proxy.listunspent(minconf, maxconf)]
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def keypoolrefill(self):
-		"Fills the keypool, requires wallet passphrase to be set."
-		try:
-			self.proxy.keypoolrefill()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def walletpassphrase(self, passphrase, timeout, dont_raise=False):
-		"""
-        Stores the wallet decryption key in memory for <timeout> seconds.
-
-        - *passphrase* -- The wallet passphrase.
-
-        - *timeout* -- Time in seconds to keep the wallet unlocked
-                    (by keeping the passphrase in memory).
-
-        - *dont_raise* -- instead of raising `~mmgen.rpc.exceptions.WalletPassphraseIncorrect`
-                       return False.
-		"""
-		try:
-			self.proxy.walletpassphrase(passphrase, timeout)
-			return True
-		except JSONRPCException as e:
-			json_exception = _wrap_exception(e.error)
-			if dont_raise:
-				if isinstance(json_exception, WalletPassphraseIncorrect):
-					return False
-				elif isinstance(json_exception, WalletAlreadyUnlocked):
-					return True
-			raise json_exception
-
-	def walletlock(self):
-		"""
-        Removes the wallet encryption key from memory, locking the wallet.
-        After calling this method, you will need to call walletpassphrase
-        again before being able to call any methods which require the wallet
-        to be unlocked.
-		"""
-		try:
-			return self.proxy.walletlock()
-		except JSONRPCException as e:
-			raise _wrap_exception(e.error)
-
-	def walletpassphrasechange(self, oldpassphrase, newpassphrase, dont_raise=False):
-		"""
-        Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.
-
-        Arguments:
-
-        - *dont_raise* -- instead of raising
-               `~mmgen.rpc.exceptions.WalletPassphraseIncorrect` return False.
-		"""
-		try:
-			self.proxy.walletpassphrasechange(oldpassphrase, newpassphrase)
-			return True
-		except JSONRPCException as e:
-			json_exception = _wrap_exception(e.error)
-			if dont_raise and isinstance(json_exception, WalletPassphraseIncorrect):
-				return False
-			raise json_exception

+ 0 - 168
mmgen/rpc/data.py

@@ -1,168 +0,0 @@
-# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-"""
-Bitcoin RPC service, data objects.
-"""
-from mmgen.rpc.util import DStruct
-
-
-class ServerInfo(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.getinfo`.
-
-    - *errors* -- Number of errors.
-
-    - *blocks* -- Number of blocks.
-
-    - *paytxfee* -- Amount of transaction fee to pay.
-
-    - *keypoololdest* -- Oldest key in keypool.
-
-    - *genproclimit* -- Processor limit for generation.
-
-    - *connections* -- Number of connections to other clients.
-
-    - *difficulty* -- Current generating difficulty.
-
-    - *testnet* -- True if connected to testnet, False if on real network.
-
-    - *version* -- Bitcoin client version.
-
-    - *proxy* -- Proxy configured in client.
-
-    - *hashespersec* -- Number of hashes per second (if generation enabled).
-
-    - *balance* -- Total current server balance.
-
-    - *generate* -- True if generation enabled, False if not.
-
-    - *unlocked_until* -- Timestamp (seconds since epoch) after which the wallet
-                          will be/was locked (if wallet encryption is enabled).
-
-    """
-
-
-class AccountInfo(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.listreceivedbyaccount`.
-
-    - *account* -- The account of the receiving address.
-
-    - *amount* -- Total amount received by the address.
-
-    - *confirmations* -- Number of confirmations of the most recent transaction included.
-
-    """
-
-
-class AddressInfo(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.listreceivedbyaddress`.
-
-    - *address* -- Receiving address.
-
-    - *account* -- The account of the receiving address.
-
-    - *amount* -- Total amount received by the address.
-
-    - *confirmations* -- Number of confirmations of the most recent transaction included.
-
-    """
-
-
-class TransactionInfo(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.listtransactions`.
-
-    - *account* -- account name.
-
-    - *address* -- the address bitcoins were sent to, or received from.
-
-    - *category* -- will be generate, send, receive, or move.
-
-    - *amount* -- amount of transaction.
-
-    - *fee* -- Fee (if any) paid (only for send transactions).
-
-    - *confirmations* -- number of confirmations (only for generate/send/receive).
-
-    - *txid* -- transaction ID (only for generate/send/receive).
-
-    - *otheraccount* -- account funds were moved to or from (only for move).
-
-    - *message* -- message associated with transaction (only for send).
-
-    - *to* -- message-to associated with transaction (only for send).
-    """
-
-
-class AddressValidation(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.validateaddress`.
-
-    - *isvalid* -- Validatity of address (:const:`True` or :const:`False`).
-
-    - *ismine* -- :const:`True` if the address is in the server's wallet.
-
-    - *address* -- Bitcoin address.
-
-    """
-
-
-class WorkItem(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.getwork`.
-
-    - *midstate* -- Precomputed hash state after hashing the first half of the data.
-
-    - *data* -- Block data.
-
-    - *hash1* -- Formatted hash buffer for second hash.
-
-    - *target* -- Little endian hash target.
-
-    """
-
-
-class MiningInfo(DStruct):
-    """
-    Information object returned by :func:`~mmgen.rpc.connection.BitcoinConnection.getmininginfo`.
-
-    - *blocks* -- Number of blocks.
-
-    - *currentblocksize* -- Size of current block.
-
-    - *currentblocktx* -- Number of transactions in current block.
-
-    - *difficulty* -- Current generating difficulty.
-
-    - *errors* -- Number of errors.
-
-    - *generate* -- True if generation enabled, False if not.
-
-    - *genproclimit* -- Processor limit for generation.
-
-    - *hashespersec* -- Number of hashes per second (if generation enabled).
-
-    - *pooledtx* -- Number of pooled transactions.
-
-    - *testnet* -- True if connected to testnet, False if on real network.
-
-    """

+ 0 - 203
mmgen/rpc/exceptions.py

@@ -1,203 +0,0 @@
-# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-"""
-Exception definitions.
-"""
-
-
-class BitcoinException(Exception):
-	"""
-    Base class for exceptions received from Bitcoin server.
-
-    - *code* -- Error code from ``bitcoind``.
-	"""
-	# Standard JSON-RPC 2.0 errors
-	INVALID_REQUEST  = -32600,
-	METHOD_NOT_FOUND = -32601,
-	INVALID_PARAMS   = -32602,
-	INTERNAL_ERROR   = -32603,
-	PARSE_ERROR      = -32700,
-
-	# General application defined errors
-	MISC_ERROR                  = -1  # std::exception thrown in command handling
-	FORBIDDEN_BY_SAFE_MODE      = -2  # Server is in safe mode, and command is not allowed in safe mode
-	TYPE_ERROR                  = -3  # Unexpected type was passed as parameter
-	INVALID_ADDRESS_OR_KEY      = -5  # Invalid address or key
-	OUT_OF_MEMORY               = -7  # Ran out of memory during operation
-	INVALID_PARAMETER           = -8  # Invalid, missing or duplicate parameter
-	DATABASE_ERROR              = -20 # Database error
-	DESERIALIZATION_ERROR       = -22 # Error parsing or validating structure in raw format
-
-	# P2P client errors
-	CLIENT_NOT_CONNECTED        = -9  # Bitcoin is not connected
-	CLIENT_IN_INITIAL_DOWNLOAD  = -10 # Still downloading initial blocks
-
-	# Wallet errors
-	WALLET_ERROR                = -4  # Unspecified problem with wallet (key not found etc.)
-	WALLET_INSUFFICIENT_FUNDS   = -6  # Not enough funds in wallet or account
-	WALLET_INVALID_ACCOUNT_NAME = -11 # Invalid account name
-	WALLET_KEYPOOL_RAN_OUT      = -12 # Keypool ran out, call keypoolrefill first
-	WALLET_UNLOCK_NEEDED        = -13 # Enter the wallet passphrase with walletpassphrase first
-	WALLET_PASSPHRASE_INCORRECT = -14 # The wallet passphrase entered was incorrect
-	WALLET_WRONG_ENC_STATE      = -15 # Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
-	WALLET_ENCRYPTION_FAILED    = -16 # Failed to encrypt the wallet
-	WALLET_ALREADY_UNLOCKED     = -17 # Wallet is already unlocked
-
-	def __init__(self, error):
-		Exception.__init__(self, error['message'])
-		self.code = error['code']
-
-
-##### General application defined errors
-class SafeMode(BitcoinException):
-	"""
-    Operation denied in safe mode (run ``bitcoind`` with ``-disablesafemode``).
-	"""
-
-
-class JSONTypeError(BitcoinException):
-	"""
-    Unexpected type was passed as parameter
-	"""
-InvalidAmount = JSONTypeError  # Backwards compatibility
-
-
-class InvalidAddressOrKey(BitcoinException):
-	"""
-    Invalid address or key.
-	"""
-InvalidTransactionID = InvalidAddressOrKey  # Backwards compatibility
-
-
-class OutOfMemory(BitcoinException):
-	"""
-    Out of memory during operation.
-	"""
-
-
-class InvalidParameter(BitcoinException):
-	"""
-    Invalid parameter provided to RPC call.
-	"""
-
-
-##### Client errors
-class ClientException(BitcoinException):
-	"""
-    P2P network error.
-    This exception is never raised but functions as a superclass
-    for other P2P client exceptions.
-	"""
-
-
-class NotConnected(ClientException):
-	"""
-    Not connected to any peers.
-	"""
-
-
-class DownloadingBlocks(ClientException):
-	"""
-    Client is still downloading blocks.
-	"""
-
-
-##### Wallet errors
-class WalletError(BitcoinException):
-	"""
-    Unspecified problem with wallet (key not found etc.)
-	"""
-SendError = WalletError  # Backwards compatibility
-
-class InsufficientFunds(WalletError):
-	"""
-    Insufficient funds to complete transaction in wallet or account
-	"""
-
-class InvalidAccountName(WalletError):
-	"""
-    Invalid account name
-	"""
-
-
-class KeypoolRanOut(WalletError):
-	"""
-    Keypool ran out, call keypoolrefill first
-	"""
-
-
-class WalletUnlockNeeded(WalletError):
-	"""
-    Enter the wallet passphrase with walletpassphrase first
-	"""
-
-
-class WalletPassphraseIncorrect(WalletError):
-	"""
-    The wallet passphrase entered was incorrect
-	"""
-
-
-class WalletWrongEncState(WalletError):
-	"""
-    Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
-	"""
-
-
-class WalletEncryptionFailed(WalletError):
-	"""
-    Failed to encrypt the wallet
-	"""
-
-
-class WalletAlreadyUnlocked(WalletError):
-	"""
-    Wallet is already unlocked
-	"""
-
-
-# For convenience, we define more specific exception classes
-# for the more common errors.
-_exception_map = {
-	BitcoinException.FORBIDDEN_BY_SAFE_MODE: SafeMode,
-	BitcoinException.TYPE_ERROR: JSONTypeError,
-	BitcoinException.WALLET_ERROR: WalletError,
-	BitcoinException.INVALID_ADDRESS_OR_KEY: InvalidAddressOrKey,
-	BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
-	BitcoinException.OUT_OF_MEMORY: OutOfMemory,
-	BitcoinException.INVALID_PARAMETER: InvalidParameter,
-	BitcoinException.CLIENT_NOT_CONNECTED: NotConnected,
-	BitcoinException.CLIENT_IN_INITIAL_DOWNLOAD: DownloadingBlocks,
-	BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
-	BitcoinException.WALLET_INVALID_ACCOUNT_NAME: InvalidAccountName,
-	BitcoinException.WALLET_KEYPOOL_RAN_OUT: KeypoolRanOut,
-	BitcoinException.WALLET_UNLOCK_NEEDED: WalletUnlockNeeded,
-	BitcoinException.WALLET_PASSPHRASE_INCORRECT: WalletPassphraseIncorrect,
-	BitcoinException.WALLET_WRONG_ENC_STATE: WalletWrongEncState,
-	BitcoinException.WALLET_ENCRYPTION_FAILED: WalletEncryptionFailed,
-	BitcoinException.WALLET_ALREADY_UNLOCKED: WalletAlreadyUnlocked,
-}
-
-
-def _wrap_exception(error):
-	"""
-    Convert a JSON error object to a more specific Bitcoin exception.
-	"""
-	return _exception_map.get(error['code'], BitcoinException)(error)

+ 0 - 142
mmgen/rpc/proxy.py

@@ -1,142 +0,0 @@
-"""
-  Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
-  Added http_timeout from mmgen.globalvars
-
-  Previous copyright from bitcoin-python/proxy.py:
-
-  Copyright 2011 Jeff Garzik
-
-  AuthServiceProxy has the following improvements over python-jsonrpc's
-  ServiceProxy class:
-
-  - HTTP connections persist for the life of the AuthServiceProxy object
-    (if server supports HTTP/1.1)
-  - sends protocol 'version', per JSON-RPC 1.1
-  - sends proper, incrementing 'id'
-  - sends Basic HTTP authentication headers
-  - parses all JSON numbers that look like floats as Decimal
-  - uses standard Python json lib
-
-  Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
-
-  Copyright (c) 2007 Jan-Klaas Kollhof
-
-  This file is part of jsonrpc.
-
-  jsonrpc is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  This software is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with this software; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-"""
-
-try:
-	import http.client as httplib
-except ImportError:
-	import httplib
-import base64
-import json
-import decimal
-try:
-	import urllib.parse as urlparse
-except ImportError:
-	import urlparse
-
-USER_AGENT = "AuthServiceProxy/0.1"
-
-class JSONRPCException(Exception):
-	def __init__(self, rpcError):
-		Exception.__init__(self)
-		self.error = rpcError
-
-
-import mmgen.globalvars as g
-
-class AuthServiceProxy(object):
-	def __init__(self, serviceURL, serviceName = None):
-
-		self.__serviceURL = serviceURL
-		self.__serviceName = serviceName
-		self.__url = urlparse.urlparse(serviceURL)
-		if self.__url.port is None:
-			port = 80
-		else:
-			port = self.__url.port
-		self.__idcnt = 0
-		authpair = "%s:%s" % (self.__url.username, self.__url.password)
-		authpair = authpair.encode('utf8')
-		self.__authhdr = "Basic ".encode('utf8') + base64.b64encode(authpair)
-
-		http_timeout = g.http_timeout
-
-		if self.__url.scheme == 'https':
-			self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
-					None, None, False, timeout=http_timeout)
-		else:
-			self.__conn = httplib.HTTPConnection(self.__url.hostname, port,
-					False, timeout=http_timeout)
-
-	def __getattr__(self, name):
-		if self.__serviceName != None:
-			name = "%s.%s" % (self.__serviceName, name)
-		return AuthServiceProxy(self.__serviceURL, name)
-
-	def __call__(self, *args):
-		self.__idcnt += 1
-
-		postdata = json.dumps({
-				'version': '1.1',
-				'method': self.__serviceName,
-				'params': args,
-				'id': self.__idcnt})
-		try:
-			self.__conn.request('POST', self.__url.path, postdata,
-					{ 'Host' : self.__url.hostname,
-					'User-Agent' : USER_AGENT,
-					'Authorization' : self.__authhdr,
-					'Content-type' : 'application/json' })
-		except:
-			from mmgen.util import die,red
-			die(1,red("Unable to connect to bitcoind"))
-
-		httpresp = self.__conn.getresponse()
-		if httpresp is None:
-			raise JSONRPCException({
-					'code' : -342, 'message' : 'missing HTTP response from server'})
-
-		resp = httpresp.read()
-		resp = resp.decode('utf8')
-		resp = json.loads(resp, parse_float=decimal.Decimal)
-		if 'error' in resp and resp['error'] != None:
-			raise JSONRPCException(resp['error'])
-		elif 'result' not in resp:
-			raise JSONRPCException({
-					'code' : -343, 'message' : 'missing JSON-RPC result'})
-		else:
-			return resp['result']
-
-	def _batch(self, rpc_call_list):
-		postdata = json.dumps(list(rpc_call_list))
-		self.__conn.request('POST', self.__url.path, postdata,
-				{ 'Host' : self.__url.hostname,
-				'User-Agent' : USER_AGENT,
-				'Authorization' : self.__authhdr,
-				'Content-type' : 'application/json' })
-
-		httpresp = self.__conn.getresponse()
-		if httpresp is None:
-			raise JSONRPCException({
-					'code' : -342, 'message' : 'missing HTTP response from server'})
-
-		resp = httpresp.read()
-		resp = resp.decode('utf8')
-		resp = json.loads(resp, parse_float=decimal.Decimal)
-		return resp

+ 0 - 49
mmgen/rpc/util.py

@@ -1,49 +0,0 @@
-# Copyright (c) 2010 Witchspace <witchspace81@gmail.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-"""Generic utilities used by bitcoin client library."""
-from copy import copy
-
-
-class DStruct(object):
-	"""
-    Simple dynamic structure, like :const:`collections.namedtuple` but more flexible
-    (and less memory-efficient)
-	"""
-	# Default arguments. Defaults are *shallow copied*, to allow defaults such as [].
-	_fields = []
-	_defaults = {}
-
-	def __init__(self, *args_t, **args_d):
-		# order
-		if len(args_t) > len(self._fields):
-			raise TypeError("Number of arguments is larger than of predefined fields")
-		# Copy default values
-		for (k, v) in self._defaults.iteritems():
-			self.__dict__[k] = copy(v)
-		# Set pass by value arguments
-		self.__dict__.update(zip(self._fields, args_t))
-		# dict
-		self.__dict__.update(args_d)
-
-	def __repr__(self):
-		return '{module}.{classname}({slots})'.format(
-			module=self.__class__.__module__, classname=self.__class__.__name__,
-			slots=", ".join('{k}={v!r}'.format(k=k, v=v) for k, v in
-							self.__dict__.iteritems()))

+ 202 - 236
mmgen/seed.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -19,22 +19,21 @@
 """
 """
 seed.py:  Seed-related classes and methods for the MMGen suite
 seed.py:  Seed-related classes and methods for the MMGen suite
 """
 """
-import sys,os
+
+import os
 from binascii import hexlify,unhexlify
 from binascii import hexlify,unhexlify
 
 
-import mmgen.globalvars as g
-import mmgen.opt as opt
+from mmgen.common import *
 from mmgen.bitcoin import b58encode_pad,b58decode_pad,b58_lens
 from mmgen.bitcoin import b58encode_pad,b58decode_pad,b58_lens
 from mmgen.obj import *
 from mmgen.obj import *
 from mmgen.filename import *
 from mmgen.filename import *
-from mmgen.util import *
 from mmgen.crypto import *
 from mmgen.crypto import *
 
 
 pnm = g.proj_name
 pnm = g.proj_name
 
 
 def check_usr_seed_len(seed_len):
 def check_usr_seed_len(seed_len):
 	if opt.seed_len != seed_len and 'seed_len' in opt.set_by_user:
 	if opt.seed_len != seed_len and 'seed_len' in opt.set_by_user:
-		m = "ERROR: requested seed length (%s) " + \
+		m = 'ERROR: requested seed length (%s) ' + \
 			"doesn't match seed length of source (%s)"
 			"doesn't match seed length of source (%s)"
 		die(1, m % (opt.seed_len,seed_len))
 		die(1, m % (opt.seed_len,seed_len))
 
 
@@ -45,7 +44,7 @@ class Seed(MMGenObject):
 			# Truncate random data for smaller seed lengths
 			# Truncate random data for smaller seed lengths
 			seed_bin = sha256(get_random(1033)).digest()[:opt.seed_len/8]
 			seed_bin = sha256(get_random(1033)).digest()[:opt.seed_len/8]
 		elif len(seed_bin)*8 not in g.seed_lens:
 		elif len(seed_bin)*8 not in g.seed_lens:
-			die(3,"%s: invalid seed length" % len(seed_bin))
+			die(3,'%s: invalid seed length' % len(seed_bin))
 
 
 		self.data      = seed_bin
 		self.data      = seed_bin
 		self.hexdata   = hexlify(seed_bin)
 		self.hexdata   = hexlify(seed_bin)
@@ -55,8 +54,8 @@ class Seed(MMGenObject):
 
 
 class SeedSource(MMGenObject):
 class SeedSource(MMGenObject):
 
 
-	desc = g.proj_name + " seed source"
-	file_mode = "text"
+	desc = g.proj_name + ' seed source'
+	file_mode = 'text'
 	stdin_ok = False
 	stdin_ok = False
 	ask_tty = True
 	ask_tty = True
 	no_tty  = False
 	no_tty  = False
@@ -71,8 +70,8 @@ class SeedSource(MMGenObject):
 		def die_on_opt_mismatch(opt,sstype):
 		def die_on_opt_mismatch(opt,sstype):
 			opt_sstype = cls.fmt_code_to_sstype(opt)
 			opt_sstype = cls.fmt_code_to_sstype(opt)
 			compare_or_die(
 			compare_or_die(
-				opt_sstype.__name__, "input format requested on command line",
-				sstype.__name__,     "input file format"
+				opt_sstype.__name__, 'input format requested on command line',
+				sstype.__name__,     'input file format'
 			)
 			)
 
 
 		if ss:
 		if ss:
@@ -83,31 +82,31 @@ class SeedSource(MMGenObject):
 			me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
 			me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
 			me.seed = ss.seed
 			me.seed = ss.seed
 			me.ss_in = ss
 			me.ss_in = ss
-			me.op = ("conv","pwchg_new")[int(passchg)]
+			me.op = ('conv','pwchg_new')[bool(passchg)]
 		elif fn or opt.hidden_incog_input_params:
 		elif fn or opt.hidden_incog_input_params:
 			if fn:
 			if fn:
 				f = Filename(fn)
 				f = Filename(fn)
 				sstype = cls.ext_to_sstype(f.ext)
 				sstype = cls.ext_to_sstype(f.ext)
 			else:
 			else:
-				fn = opt.hidden_incog_input_params.split(",")[0]
-				f  = Filename(fn,ftype="hincog")
-				sstype = cls.fmt_code_to_sstype("hincog")
+				fn = opt.hidden_incog_input_params.split(',')[0]
+				f  = Filename(fn,ftype='hincog')
+				sstype = cls.fmt_code_to_sstype('hincog')
 
 
 			if opt.in_fmt and not ignore_in_fmt:
 			if opt.in_fmt and not ignore_in_fmt:
 				die_on_opt_mismatch(opt.in_fmt,sstype)
 				die_on_opt_mismatch(opt.in_fmt,sstype)
 
 
 			me = super(cls,cls).__new__(sstype)
 			me = super(cls,cls).__new__(sstype)
 			me.infile = f
 			me.infile = f
-			me.op = ("old","pwchg_old")[int(passchg)]
+			me.op = ('old','pwchg_old')[bool(passchg)]
 		elif opt.in_fmt:  # Input format
 		elif opt.in_fmt:  # Input format
 			sstype = cls.fmt_code_to_sstype(opt.in_fmt)
 			sstype = cls.fmt_code_to_sstype(opt.in_fmt)
 			me = super(cls,cls).__new__(sstype)
 			me = super(cls,cls).__new__(sstype)
-			me.op = ("old","pwchg_old")[int(passchg)]
+			me.op = ('old','pwchg_old')[bool(passchg)]
 		else: # Called with no inputs - initialize with random seed
 		else: # Called with no inputs - initialize with random seed
 			sstype = cls.fmt_code_to_sstype(opt.out_fmt)
 			sstype = cls.fmt_code_to_sstype(opt.out_fmt)
 			me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
 			me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
 			me.seed = Seed(seed_bin=seed or None)
 			me.seed = Seed(seed_bin=seed or None)
-			me.op = "new"
+			me.op = 'new'
 
 
 		return me
 		return me
 
 
@@ -130,38 +129,38 @@ class SeedSource(MMGenObject):
 			self._decrypt_retry()
 			self._decrypt_retry()
 		else:
 		else:
 			if not self.stdin_ok:
 			if not self.stdin_ok:
-				die(1,"Reading from standard input not supported for %s format"
+				die(1,'Reading from standard input not supported for %s format'
 						% self.desc)
 						% self.desc)
 			self._deformat_retry()
 			self._deformat_retry()
 			self._decrypt_retry()
 			self._decrypt_retry()
 
 
-		m = ("",", seed length %s" % self.seed.length)[int(self.seed.length != 256)]
-		qmsg("Valid %s for Seed ID %s%s" % (self.desc,self.seed.sid,m))
+		m = ('',', seed length %s' % self.seed.length)[self.seed.length!=256]
+		qmsg('Valid %s for Seed ID %s%s' % (self.desc,self.seed.sid,m))
 
 
 	def _get_data(self):
 	def _get_data(self):
 		if hasattr(self,'infile'):
 		if hasattr(self,'infile'):
 			self.fmt_data = get_data_from_file(self.infile.name,self.desc,
 			self.fmt_data = get_data_from_file(self.infile.name,self.desc,
-								binary=self.file_mode=="binary")
+								binary=self.file_mode=='binary')
 		else:
 		else:
 			self.fmt_data = get_data_from_user(self.desc)
 			self.fmt_data = get_data_from_user(self.desc)
 
 
 	def _deformat_once(self):
 	def _deformat_once(self):
 		self._get_data()
 		self._get_data()
 		if not self._deformat():
 		if not self._deformat():
-			die(2,"Invalid format for input data")
+			die(2,'Invalid format for input data')
 
 
 	def _deformat_retry(self):
 	def _deformat_retry(self):
 		while True:
 		while True:
 			self._get_data()
 			self._get_data()
 			if self._deformat(): break
 			if self._deformat(): break
-			msg("Trying again...")
+			msg('Trying again...')
 
 
 	def _decrypt_retry(self):
 	def _decrypt_retry(self):
 		while True:
 		while True:
 			if self._decrypt(): break
 			if self._decrypt(): break
 			if opt.passwd_file:
 			if opt.passwd_file:
-				die(2,"Passphrase from password file, so exiting")
-			msg("Trying again...")
+				die(2,'Passphrase from password file, so exiting')
+			msg('Trying again...')
 
 
 	subclasses = []
 	subclasses = []
 
 
@@ -185,7 +184,7 @@ class SeedSource(MMGenObject):
 	def fmt_code_to_sstype(cls,fmt_code):
 	def fmt_code_to_sstype(cls,fmt_code):
 		if not fmt_code: return None
 		if not fmt_code: return None
 		for c in cls._get_subclasses():
 		for c in cls._get_subclasses():
-			if hasattr(c,"fmt_codes") and fmt_code in c.fmt_codes:
+			if hasattr(c,'fmt_codes') and fmt_code in c.fmt_codes:
 				return c
 				return c
 		return None
 		return None
 
 
@@ -193,20 +192,20 @@ class SeedSource(MMGenObject):
 	def ext_to_sstype(cls,ext):
 	def ext_to_sstype(cls,ext):
 		if not ext: return None
 		if not ext: return None
 		for c in cls._get_subclasses():
 		for c in cls._get_subclasses():
-			if hasattr(c,"ext") and ext == c.ext:
+			if hasattr(c,'ext') and ext == c.ext:
 				return c
 				return c
 		return None
 		return None
 
 
 	@classmethod
 	@classmethod
 	def format_fmt_codes(cls):
 	def format_fmt_codes(cls):
-		d = [(c.__name__,",".join(c.fmt_codes)) for c in cls._get_subclasses()
-				if hasattr(c,"fmt_codes")]
+		d = [(c.__name__,','.join(c.fmt_codes)) for c in cls._get_subclasses()
+				if hasattr(c,'fmt_codes')]
 		w = max([len(a) for a,b in d])
 		w = max([len(a) for a,b in d])
-		ret = ["{:<{w}}  {}".format(a,b,w=w) for a,b in [
-			("Format","Valid codes"),
-			("------","-----------")
+		ret = ['{:<{w}}  {}'.format(a,b,w=w) for a,b in [
+			('Format','Valid codes'),
+			('------','-----------')
 			] + sorted(d)]
 			] + sorted(d)]
-		return "\n".join(ret) + "\n"
+		return '\n'.join(ret) + '\n'
 
 
 	def get_fmt_data(self):
 	def get_fmt_data(self):
 		self._format()
 		self._format()
@@ -218,7 +217,7 @@ class SeedSource(MMGenObject):
 			'desc':     self.desc,
 			'desc':     self.desc,
 			'ask_tty':  self.ask_tty,
 			'ask_tty':  self.ask_tty,
 			'no_tty':   self.no_tty,
 			'no_tty':   self.no_tty,
-			'binary':   self.file_mode == "binary"
+			'binary':   self.file_mode == 'binary'
 		}
 		}
 		write_data_to_file(self._filename(),self.fmt_data,**kwargs)
 		write_data_to_file(self._filename(),self.fmt_data,**kwargs)
 
 
@@ -240,17 +239,17 @@ an empty passphrase, just hit ENTER twice.
 	""".strip()
 	""".strip()
 	}
 	}
 
 
-	def _get_hash_preset_from_user(self,hp,desc_suf=""):
+	def _get_hash_preset_from_user(self,hp,desc_suf=''):
 # 					hp=a,
 # 					hp=a,
-		n = ("","old ")[int(self.op=="pwchg_old")]
-		m,n = (("to accept the default",n),("to reuse the old","new "))[
-						int(self.op=="pwchg_new")]
+		n = ('','old ')[self.op=='pwchg_old']
+		m,n = (('to accept the default',n),('to reuse the old','new '))[
+						int(self.op=='pwchg_new')]
 		fs = "Enter {}hash preset for {}{}{},\n or hit ENTER {} value ('{}'): "
 		fs = "Enter {}hash preset for {}{}{},\n or hit ENTER {} value ('{}'): "
 		p = fs.format(
 		p = fs.format(
 			n,
 			n,
-			("","new ")[int(self.op=="new")],
+			('','new ')[self.op=='new'],
 			self.desc,
 			self.desc,
-			(""," "+desc_suf)[int(bool(desc_suf))],
+			('',' '+desc_suf)[bool(desc_suf)],
 			m,
 			m,
 			hp
 			hp
 		)
 		)
@@ -261,14 +260,14 @@ an empty passphrase, just hit ENTER twice.
 					self.ssdata.hash_preset = ret
 					self.ssdata.hash_preset = ret
 					return ret
 					return ret
 				else:
 				else:
-					msg("Invalid input.  Valid choices are %s" %
-							", ".join(sorted(g.hash_presets.keys())))
+					msg('Invalid input.  Valid choices are %s' %
+							', '.join(sorted(g.hash_presets.keys())))
 			else:
 			else:
 				self.ssdata.hash_preset = hp
 				self.ssdata.hash_preset = hp
 				return hp
 				return hp
 
 
-	def _get_hash_preset(self,desc_suf=""):
-		if hasattr(self,"ss_in") and hasattr(self.ss_in.ssdata,"hash_preset"):
+	def _get_hash_preset(self,desc_suf=''):
+		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
 			old_hp = self.ss_in.ssdata.hash_preset
 			old_hp = self.ss_in.ssdata.hash_preset
 			if opt.keep_hash_preset:
 			if opt.keep_hash_preset:
 				qmsg("Reusing hash preset '%s' at user request" % old_hp)
 				qmsg("Reusing hash preset '%s' at user request" % old_hp)
@@ -280,9 +279,9 @@ an empty passphrase, just hit ENTER twice.
 			else: # Prompt, using old value as default
 			else: # Prompt, using old value as default
 				hp = self._get_hash_preset_from_user(old_hp,desc_suf)
 				hp = self._get_hash_preset_from_user(old_hp,desc_suf)
 
 
-			if (not opt.keep_hash_preset) and self.op == "pwchg_new":
-				m = ("changed to '%s'" % hp,"unchanged")[int(hp==old_hp)]
-				qmsg("Hash preset %s" % m)
+			if (not opt.keep_hash_preset) and self.op == 'pwchg_new':
+				m = ("changed to '%s'" % hp,'unchanged')[hp==old_hp]
+				qmsg('Hash preset %s' % m)
 		elif 'hash_preset' in opt.set_by_user:
 		elif 'hash_preset' in opt.set_by_user:
 			self.ssdata.hash_preset = opt.hash_preset
 			self.ssdata.hash_preset = opt.hash_preset
 			qmsg("Using hash preset '%s' requested on command line"%opt.hash_preset)
 			qmsg("Using hash preset '%s' requested on command line"%opt.hash_preset)
@@ -290,44 +289,43 @@ an empty passphrase, just hit ENTER twice.
 			self._get_hash_preset_from_user(opt.hash_preset,desc_suf)
 			self._get_hash_preset_from_user(opt.hash_preset,desc_suf)
 
 
 	def _get_new_passphrase(self):
 	def _get_new_passphrase(self):
-		desc = "{}passphrase for {}{}".format(
-				("","new ")[int(self.op=="pwchg_new")],
-				("","new ")[int(self.op in ("new","conv"))],
+		desc = '{}passphrase for {}{}'.format(
+				('','new ')[self.op=='pwchg_new'],
+				('','new ')[self.op in ('new','conv')],
 				self.desc
 				self.desc
 			)
 			)
 		if opt.passwd_file:
 		if opt.passwd_file:
 			w = pwfile_reuse_warning()
 			w = pwfile_reuse_warning()
-			pw = " ".join(get_words_from_file(opt.passwd_file,desc,silent=w))
+			pw = ' '.join(get_words_from_file(opt.passwd_file,desc,silent=w))
 		elif opt.echo_passphrase:
 		elif opt.echo_passphrase:
-			pw = " ".join(get_words_from_user("Enter %s: " % desc))
+			pw = ' '.join(get_words_from_user('Enter %s: ' % desc))
 		else:
 		else:
 			for i in range(g.passwd_max_tries):
 			for i in range(g.passwd_max_tries):
-				pw = " ".join(get_words_from_user("Enter %s: " % desc))
-				pw2 = " ".join(get_words_from_user("Repeat passphrase: "))
-				dmsg("Passphrases: [%s] [%s]" % (pw,pw2))
+				pw = ' '.join(get_words_from_user('Enter %s: ' % desc))
+				pw2 = ' '.join(get_words_from_user('Repeat passphrase: '))
+				dmsg('Passphrases: [%s] [%s]' % (pw,pw2))
 				if pw == pw2:
 				if pw == pw2:
-					vmsg("Passphrases match"); break
-				else: msg("Passphrases do not match.  Try again.")
+					vmsg('Passphrases match'); break
+				else: msg('Passphrases do not match.  Try again.')
 			else:
 			else:
-				msg("User failed to duplicate passphrase in %s attempts" %
+				die(2,'User failed to duplicate passphrase in %s attempts' %
 						g.passwd_max_tries)
 						g.passwd_max_tries)
-				sys.exit(2)
 
 
-		if pw == "": qmsg("WARNING: Empty passphrase")
+		if pw == '': qmsg('WARNING: Empty passphrase')
 		self.ssdata.passwd = pw
 		self.ssdata.passwd = pw
 		return pw
 		return pw
 
 
-	def _get_passphrase(self,desc_suf=""):
-		desc ="{}passphrase for {}{}".format(
-			("","old ")[int(self.op=="pwchg_old")],
+	def _get_passphrase(self,desc_suf=''):
+		desc ='{}passphrase for {}{}'.format(
+			('','old ')[self.op=='pwchg_old'],
 			self.desc,
 			self.desc,
-			(""," "+desc_suf)[int(bool(desc_suf))]
+			('',' '+desc_suf)[bool(desc_suf)]
 		)
 		)
 		if opt.passwd_file:
 		if opt.passwd_file:
 			w = pwfile_reuse_warning()
 			w = pwfile_reuse_warning()
-			ret = " ".join(get_words_from_file(opt.passwd_file,desc,silent=w))
+			ret = ' '.join(get_words_from_file(opt.passwd_file,desc,silent=w))
 		else:
 		else:
-			ret = " ".join(get_words_from_user("Enter %s: " % desc))
+			ret = ' '.join(get_words_from_user('Enter %s: ' % desc))
 		self.ssdata.passwd = ret
 		self.ssdata.passwd = ret
 
 
 	def _get_first_pw_and_hp_and_encrypt_seed(self):
 	def _get_first_pw_and_hp_and_encrypt_seed(self):
@@ -338,12 +336,12 @@ an empty passphrase, just hit ENTER twice.
 			old_pw = self.ss_in.ssdata.passwd
 			old_pw = self.ss_in.ssdata.passwd
 			if opt.keep_passphrase:
 			if opt.keep_passphrase:
 				d.passwd = old_pw
 				d.passwd = old_pw
-				qmsg("Reusing passphrase at user request")
+				qmsg('Reusing passphrase at user request')
 			else:
 			else:
 				pw = self._get_new_passphrase()
 				pw = self._get_new_passphrase()
-				if self.op == "pwchg_new":
-					m = ("changed","unchanged")[int(pw==old_pw)]
-					qmsg("Passphrase %s" % m)
+				if self.op == 'pwchg_new':
+					m = ('changed','unchanged')[pw==old_pw]
+					qmsg('Passphrase %s' % m)
 		else:
 		else:
 			qmsg(self.msg['choose_passphrase'] % (self.desc,d.hash_preset))
 			qmsg(self.msg['choose_passphrase'] % (self.desc,d.hash_preset))
 			self._get_new_passphrase()
 			self._get_new_passphrase()
@@ -357,12 +355,12 @@ an empty passphrase, just hit ENTER twice.
 class Mnemonic (SeedSourceUnenc):
 class Mnemonic (SeedSourceUnenc):
 
 
 	stdin_ok = True
 	stdin_ok = True
-	fmt_codes = "mmwords","words","mnemonic","mnem","mn","m"
-	desc = "mnemonic data"
-	ext = "mmwords"
+	fmt_codes = 'mmwords','words','mnemonic','mnem','mn','m'
+	desc = 'mnemonic data'
+	ext = 'mmwords'
 	wl_checksums = {
 	wl_checksums = {
-		"electrum": '5ca31424',
-		"tirosh":   '1a5faeff'
+		'electrum': '5ca31424',
+		'tirosh':   '1a5faeff'
 	}
 	}
 	mn_base = 1626
 	mn_base = 1626
 	wordlists = sorted(wl_checksums)
 	wordlists = sorted(wl_checksums)
@@ -377,7 +375,7 @@ class Mnemonic (SeedSourceUnenc):
 	def baseNtohex(base,words,wl,pad=0):
 	def baseNtohex(base,words,wl,pad=0):
 		deconv =  [wl.index(words[::-1][i])*(base**i)
 		deconv =  [wl.index(words[::-1][i])*(base**i)
 					for i in range(len(words))]
 					for i in range(len(words))]
-		ret = ("{:0%sx}" % pad).format(sum(deconv))
+		ret = ('{:0%sx}' % pad).format(sum(deconv))
 		return ('','0')[len(ret) % 2] + ret
 		return ('','0')[len(ret) % 2] + ret
 
 
 	@staticmethod
 	@staticmethod
@@ -402,28 +400,28 @@ class Mnemonic (SeedSourceUnenc):
 	def get_wordlist(cls,wordlist=None):
 	def get_wordlist(cls,wordlist=None):
 		wordlist = wordlist or g.default_wordlist
 		wordlist = wordlist or g.default_wordlist
 		if wordlist not in cls.wordlists:
 		if wordlist not in cls.wordlists:
-			die(1,'"%s": invalid wordlist.  Valid choices: %s' %
-				(wordlist,'"'+'" "'.join(cls.wordlists)+'"'))
+			die(1,"'%s': invalid wordlist.  Valid choices: '%s'" %
+				(wordlist,"' '".join(cls.wordlists)))
 
 
-		return __import__("mmgen.mn_"+wordlist,fromlist=["words"]).words.split()
+		return __import__('mmgen.mn_'+wordlist,fromlist=['words']).words.split()
 
 
 	@classmethod
 	@classmethod
 	def check_wordlist(cls,wlname):
 	def check_wordlist(cls,wlname):
 
 
 		wl = cls.get_wordlist(wlname)
 		wl = cls.get_wordlist(wlname)
-		Msg("Wordlist: %s\nLength: %i words" % (capfirst(wlname),len(wl)))
-		new_chksum = sha256(" ".join(wl)).hexdigest()[:8]
+		Msg('Wordlist: %s\nLength: %i words' % (capfirst(wlname),len(wl)))
+		new_chksum = sha256(' '.join(wl)).hexdigest()[:8]
 
 
 		if (sorted(wl) == wl):
 		if (sorted(wl) == wl):
-			Msg("List is sorted")
+			Msg('List is sorted')
 		else:
 		else:
-			die(3,"ERROR: List is not sorted!")
+			die(3,'ERROR: List is not sorted!')
 
 
 		compare_chksums(
 		compare_chksums(
-			new_chksum,"generated checksum",
-			cls.wl_checksums[wlname],"saved checksum",
+			new_chksum,'generated checksum',
+			cls.wl_checksums[wlname],'saved checksum',
 			die_on_fail=True)
 			die_on_fail=True)
-		Msg("Checksum %s matches" % new_chksum)
+		Msg('Checksum %s matches' % new_chksum)
 
 
 	def _format(self):
 	def _format(self):
 		wl = self.get_wordlist()
 		wl = self.get_wordlist()
@@ -432,11 +430,11 @@ class Mnemonic (SeedSourceUnenc):
 
 
 		ret = self.baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
 		ret = self.baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
 		# Internal error, so just die on fail
 		# Internal error, so just die on fail
-		compare_or_die(ret,"recomputed seed",
-						seed_hex,"original",e="Internal error")
+		compare_or_die(ret,'recomputed seed',
+						seed_hex,'original',e='Internal error')
 
 
 		self.ssdata.mnemonic = mn
 		self.ssdata.mnemonic = mn
-		self.fmt_data = " ".join(mn) + "\n"
+		self.fmt_data = ' '.join(mn) + '\n'
 
 
 	def _deformat(self):
 	def _deformat(self):
 
 
@@ -444,13 +442,13 @@ class Mnemonic (SeedSourceUnenc):
 		wl = self.get_wordlist()
 		wl = self.get_wordlist()
 
 
 		if len(mn) not in g.mn_lens:
 		if len(mn) not in g.mn_lens:
-			msg("Invalid mnemonic (%i words).  Allowed numbers of words: %s" %
-					(len(mn),", ".join([str(i) for i in g.mn_lens])))
+			msg('Invalid mnemonic (%i words).  Allowed numbers of words: %s' %
+					(len(mn),', '.join([str(i) for i in g.mn_lens])))
 			return False
 			return False
 
 
 		for n,w in enumerate(mn,1):
 		for n,w in enumerate(mn,1):
 			if w not in wl:
 			if w not in wl:
-				msg("Invalid mnemonic: word #%s is not in the wordlist" % n)
+				msg('Invalid mnemonic: word #%s is not in the wordlist' % n)
 				return False
 				return False
 
 
 		seed_hex = self.baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
 		seed_hex = self.baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
@@ -458,8 +456,8 @@ class Mnemonic (SeedSourceUnenc):
 		ret = self.hextobaseN(self.mn_base,seed_hex,wl,self._hex2mn_pad(seed_hex))
 		ret = self.hextobaseN(self.mn_base,seed_hex,wl,self._hex2mn_pad(seed_hex))
 
 
 		# Internal error, so just die
 		# Internal error, so just die
-		compare_or_die(" ".join(ret),"recomputed mnemonic",
-						" ".join(mn),"original",e="Internal error")
+		compare_or_die(' '.join(ret),'recomputed mnemonic',
+						' '.join(mn),'original',e='Internal error')
 
 
 		self.seed = Seed(unhexlify(seed_hex))
 		self.seed = Seed(unhexlify(seed_hex))
 		self.ssdata.mnemonic = mn
 		self.ssdata.mnemonic = mn
@@ -469,21 +467,21 @@ class Mnemonic (SeedSourceUnenc):
 		return True
 		return True
 
 
 	def _filename(self):
 	def _filename(self):
-		return "%s[%s].%s" % (self.seed.sid,self.seed.length,self.ext)
+		return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
 
 
 
 
 class SeedFile (SeedSourceUnenc):
 class SeedFile (SeedSourceUnenc):
 
 
 	stdin_ok = True
 	stdin_ok = True
-	fmt_codes = "mmseed","seed","s"
-	desc = "seed data"
-	ext = "mmseed"
+	fmt_codes = 'mmseed','seed','s'
+	desc = 'seed data'
+	ext = 'mmseed'
 
 
 	def _format(self):
 	def _format(self):
 		b58seed = b58encode_pad(self.seed.data)
 		b58seed = b58encode_pad(self.seed.data)
 		self.ssdata.chksum = make_chksum_6(b58seed)
 		self.ssdata.chksum = make_chksum_6(b58seed)
 		self.ssdata.b58seed = b58seed
 		self.ssdata.b58seed = b58seed
-		self.fmt_data = "%s %s\n" % (
+		self.fmt_data = '%s %s\n' % (
 				self.ssdata.chksum,
 				self.ssdata.chksum,
 				split_into_cols(4,b58seed)
 				split_into_cols(4,b58seed)
 			)
 			)
@@ -493,10 +491,10 @@ class SeedFile (SeedSourceUnenc):
 		ld = self.fmt_data.split()
 		ld = self.fmt_data.split()
 
 
 		if not (7 <= len(ld) <= 12): # 6 <= padded b58 data (ld[1:]) <= 11
 		if not (7 <= len(ld) <= 12): # 6 <= padded b58 data (ld[1:]) <= 11
-			msg("Invalid data length (%s) in %s" % (len(ld),desc))
+			msg('Invalid data length (%s) in %s' % (len(ld),desc))
 			return False
 			return False
 
 
-		a,b = ld[0],"".join(ld[1:])
+		a,b = ld[0],''.join(ld[1:])
 
 
 		if not is_chksum_6(a):
 		if not is_chksum_6(a):
 			msg("'%s': invalid checksum format in %s" % (a, desc))
 			msg("'%s': invalid checksum format in %s" % (a, desc))
@@ -506,16 +504,16 @@ class SeedFile (SeedSourceUnenc):
 			msg("'%s': not a base 58 string, in %s" % (b, desc))
 			msg("'%s': not a base 58 string, in %s" % (b, desc))
 			return False
 			return False
 
 
-		vmsg_r("Validating %s checksum..." % desc)
+		vmsg_r('Validating %s checksum...' % desc)
 
 
 		if not compare_chksums(
 		if not compare_chksums(
-				a,"checksum",make_chksum_6(b),"base 58 data"):
+				a,'checksum',make_chksum_6(b),'base 58 data'):
 			return False
 			return False
 
 
 		ret = b58decode_pad(b)
 		ret = b58decode_pad(b)
 
 
 		if ret == False:
 		if ret == False:
-			msg("Invalid base-58 encoded seed: %s" % val)
+			msg('Invalid base-58 encoded seed: %s' % val)
 			return False
 			return False
 
 
 		self.seed = Seed(ret)
 		self.seed = Seed(ret)
@@ -527,27 +525,27 @@ class SeedFile (SeedSourceUnenc):
 		return True
 		return True
 
 
 	def _filename(self):
 	def _filename(self):
-		return "%s[%s].%s" % (self.seed.sid,self.seed.length,self.ext)
+		return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
 
 
 
 
 class Wallet (SeedSourceEnc):
 class Wallet (SeedSourceEnc):
 
 
-	fmt_codes = "wallet","w"
-	desc = g.proj_name + " wallet"
-	ext = "mmdat"
+	fmt_codes = 'wallet','w'
+	desc = g.proj_name + ' wallet'
+	ext = 'mmdat'
 
 
-	def _get_label_from_user(self,old_lbl=""):
-		d = ("to reuse the label '%s'" % old_lbl) if old_lbl else "for no label"
-		p = "Enter a wallet label, or hit ENTER %s: " % d
+	def _get_label_from_user(self,old_lbl=''):
+		d = ("to reuse the label '%s'" % old_lbl) if old_lbl else 'for no label'
+		p = 'Enter a wallet label, or hit ENTER %s: ' % d
 		while True:
 		while True:
 			ret = my_raw_input(p)
 			ret = my_raw_input(p)
 			if ret:
 			if ret:
 				if is_mmgen_wallet_label(ret):
 				if is_mmgen_wallet_label(ret):
 					self.ssdata.label = ret; return ret
 					self.ssdata.label = ret; return ret
 				else:
 				else:
-					msg("Invalid label.  Trying again...")
+					msg('Invalid label.  Trying again...')
 			else:
 			else:
-				ret = old_lbl or "No Label"
+				ret = old_lbl or 'No Label'
 				self.ssdata.label = ret; return ret
 				self.ssdata.label = ret; return ret
 
 
 	# nearly identical to _get_hash_preset() - factor?
 	# nearly identical to _get_hash_preset() - factor?
@@ -563,9 +561,9 @@ class Wallet (SeedSourceEnc):
 			else: # Prompt, using old value as default
 			else: # Prompt, using old value as default
 				lbl = self._get_label_from_user(old_lbl)
 				lbl = self._get_label_from_user(old_lbl)
 
 
-			if (not opt.keep_label) and self.op == "pwchg_new":
-				m = ("changed to '%s'" % lbl,"unchanged")[int(lbl==old_lbl)]
-				qmsg("Label %s" % m)
+			if (not opt.keep_label) and self.op == 'pwchg_new':
+				m = ("changed to '%s'" % lbl,'unchanged')[lbl==old_lbl]
+				qmsg('Label %s' % m)
 		elif opt.label:
 		elif opt.label:
 			qmsg("Using label '%s' requested on command line" % opt.label)
 			qmsg("Using label '%s' requested on command line" % opt.label)
 			self.ssdata.label = opt.label
 			self.ssdata.label = opt.label
@@ -576,7 +574,7 @@ class Wallet (SeedSourceEnc):
 		self._get_first_pw_and_hp_and_encrypt_seed()
 		self._get_first_pw_and_hp_and_encrypt_seed()
 		self._get_label()
 		self._get_label()
 		d = self.ssdata
 		d = self.ssdata
-		d.pw_status = ("NE","E")[int(len(d.passwd)==0)]
+		d.pw_status = ('NE','E')[len(d.passwd)==0]
 		d.timestamp = make_timestamp()
 		d.timestamp = make_timestamp()
 
 
 	def _format(self):
 	def _format(self):
@@ -586,32 +584,32 @@ class Wallet (SeedSourceEnc):
 		es_fmt = b58encode_pad(d.enc_seed)
 		es_fmt = b58encode_pad(d.enc_seed)
 		lines = (
 		lines = (
 			d.label,
 			d.label,
-			"{} {} {} {} {}".format(s.sid.lower(), d.key_id.lower(),
+			'{} {} {} {} {}'.format(s.sid.lower(), d.key_id.lower(),
 										s.length, d.pw_status, d.timestamp),
 										s.length, d.pw_status, d.timestamp),
-			"{}: {} {} {}".format(d.hash_preset,*get_hash_params(d.hash_preset)),
-			"{} {}".format(make_chksum_6(slt_fmt),split_into_cols(4,slt_fmt)),
-			"{} {}".format(make_chksum_6(es_fmt), split_into_cols(4,es_fmt))
+			'{}: {} {} {}'.format(d.hash_preset,*get_hash_params(d.hash_preset)),
+			'{} {}'.format(make_chksum_6(slt_fmt),split_into_cols(4,slt_fmt)),
+			'{} {}'.format(make_chksum_6(es_fmt), split_into_cols(4,es_fmt))
 		)
 		)
-		chksum = make_chksum_6(" ".join(lines))
-		self.fmt_data = "%s\n" % "\n".join((chksum,)+lines)
+		chksum = make_chksum_6(' '.join(lines))
+		self.fmt_data = '%s\n' % '\n'.join((chksum,)+lines)
 
 
 	def _deformat(self):
 	def _deformat(self):
 
 
 		def check_master_chksum(lines,desc):
 		def check_master_chksum(lines,desc):
 
 
 			if len(lines) != 6:
 			if len(lines) != 6:
-				msg("Invalid number of lines (%s) in %s data" %
+				msg('Invalid number of lines (%s) in %s data' %
 						(len(lines),desc))
 						(len(lines),desc))
 				return False
 				return False
 
 
 			if not is_chksum_6(lines[0]):
 			if not is_chksum_6(lines[0]):
-				msg("Incorrect master checksum (%s) in %s data" %
+				msg('Incorrect master checksum (%s) in %s data' %
 						(lines[0],desc))
 						(lines[0],desc))
 				return False
 				return False
 
 
-			chk = make_chksum_6(" ".join(lines[1:]))
-			if not compare_chksums(lines[0],"master",chk,"computed",
-						hdr="For wallet master checksum"):
+			chk = make_chksum_6(' '.join(lines[1:]))
+			if not compare_chksums(lines[0],'master',chk,'computed',
+						hdr='For wallet master checksum'):
 				return False
 				return False
 
 
 			return True
 			return True
@@ -641,26 +639,26 @@ class Wallet (SeedSourceEnc):
 
 
 		if hash_params != get_hash_params(d.hash_preset):
 		if hash_params != get_hash_params(d.hash_preset):
 			msg("Hash parameters '%s' don't match hash preset '%s'" %
 			msg("Hash parameters '%s' don't match hash preset '%s'" %
-					(" ".join(hash_params), d.hash_preset))
+					(' '.join(hash_params), d.hash_preset))
 			return False
 			return False
 
 
 		lmin,lmax = b58_lens[0],b58_lens[-1] # 22,33,44
 		lmin,lmax = b58_lens[0],b58_lens[-1] # 22,33,44
-		for i,key in (4,"salt"),(5,"enc_seed"):
-			l = lines[i].split(" ")
+		for i,key in (4,'salt'),(5,'enc_seed'):
+			l = lines[i].split(' ')
 			chk = l.pop(0)
 			chk = l.pop(0)
-			b58_val = "".join(l)
+			b58_val = ''.join(l)
 
 
 			if len(b58_val) < lmin or len(b58_val) > lmax:
 			if len(b58_val) < lmin or len(b58_val) > lmax:
-				msg("Invalid format for %s in %s: %s" % (key,self.desc,l))
+				msg('Invalid format for %s in %s: %s' % (key,self.desc,l))
 				return False
 				return False
 
 
 			if not compare_chksums(chk,key,
 			if not compare_chksums(chk,key,
-					make_chksum_6(b58_val),"computed checksum"):
+					make_chksum_6(b58_val),'computed checksum'):
 				return False
 				return False
 
 
 			val = b58decode_pad(b58_val)
 			val = b58decode_pad(b58_val)
 			if val == False:
 			if val == False:
-				msg("Invalid base 58 number: %s" % b58_val)
+				msg('Invalid base 58 number: %s' % b58_val)
 				return False
 				return False
 
 
 			setattr(d,key,val)
 			setattr(d,key,val)
@@ -670,7 +668,7 @@ class Wallet (SeedSourceEnc):
 	def _decrypt(self):
 	def _decrypt(self):
 		d = self.ssdata
 		d = self.ssdata
 		# Needed for multiple transactions with {}-txsign
 		# Needed for multiple transactions with {}-txsign
-		suf = ("",self.infile.name)[int(bool(opt.quiet))]
+		suf = ('',self.infile.name)[bool(opt.quiet)]
 		self._get_passphrase(desc_suf=suf)
 		self._get_passphrase(desc_suf=suf)
 		key = make_key(d.passwd, d.salt, d.hash_preset)
 		key = make_key(d.passwd, d.salt, d.hash_preset)
 		ret = decrypt_seed(d.enc_seed, key, d.seed_id, d.key_id)
 		ret = decrypt_seed(d.enc_seed, key, d.seed_id, d.key_id)
@@ -681,7 +679,7 @@ class Wallet (SeedSourceEnc):
 			return False
 			return False
 
 
 	def _filename(self):
 	def _filename(self):
-		return "{}-{}[{},{}].{}".format(
+		return '{}-{}[{},{}].{}'.format(
 				self.seed.sid,
 				self.seed.sid,
 				self.ssdata.key_id,
 				self.ssdata.key_id,
 				self.seed.length,
 				self.seed.length,
@@ -689,80 +687,48 @@ class Wallet (SeedSourceEnc):
 				self.ext
 				self.ext
 			)
 			)
 
 
-# 	def __str__(self):
-##	label,metadata,hash_preset,salt,enc_seed):
-# 		d = self.ssdata
-# 		s = self.seed
-# 		out = ["WALLET DATA"]
-# 		fs = "  {:18} {}"
-# 		pw_empty = "Yes" if d.metadata[3] == "E" else "No"
-# 		for i in (
-# 			("Label:",         d.label),
-# 			("Seed ID:",       s.sid),
-# 			("Key  ID:",       d.key_id),
-# 			("Seed length:",   "%s bits (%s bytes)" % (s.length,s.length/8)),
-# 			("Scrypt params:", "Preset '%s' (%s)" % (opt.hash_preset,
-# 					" ".join([str(i) for i in get_hash_params(opt.hash_preset)])
-# 					)
-# 			),
-# 			("Passphrase empty?", pw_empty),
-# 			("Timestamp:",     "%s UTC" % d.metadata[4]),
-# 		): out.append(fs.format(*i))
-#
-# 		fs = "  {:6} {}"
-# 		for i in (
-# 			("Salt:",   ""),
-# 			("  b58:",  b58encode_pad(d.salt)),
-# 			("  hex:",  hexlify(d.salt)),
-# 			("Encrypted seed:", ""),
-# 			("  b58:",  b58encode_pad(d.enc_seed)),
-# 			("  hex:",  hexlify(d.enc_seed))
-# 		): out.append(fs.format(*i))
-#
-# 		return "\n".join(out)
-
 
 
 class Brainwallet (SeedSourceEnc):
 class Brainwallet (SeedSourceEnc):
 
 
 	stdin_ok = True
 	stdin_ok = True
-	fmt_codes = "mmbrain","brainwallet","brain","bw","b"
-	desc = "brainwallet"
-	ext = "mmbrain"
+	fmt_codes = 'mmbrain','brainwallet','brain','bw','b'
+	desc = 'brainwallet'
+	ext = 'mmbrain'
 	# brainwallet warning message? TODO
 	# brainwallet warning message? TODO
 
 
 	def get_bw_params(self):
 	def get_bw_params(self):
 		# already checked
 		# already checked
-		a = opt.brain_params.split(",")
+		a = opt.brain_params.split(',')
 		return int(a[0]),a[1]
 		return int(a[0]),a[1]
 
 
 	def _deformat(self):
 	def _deformat(self):
-		self.brainpasswd = " ".join(self.fmt_data.split())
+		self.brainpasswd = ' '.join(self.fmt_data.split())
 		return True
 		return True
 
 
 	def _decrypt(self):
 	def _decrypt(self):
 		d = self.ssdata
 		d = self.ssdata
-		if hasattr(opt,"brain_params"):
+		if hasattr(opt,'brain_params'):
 			seed_len,d.hash_preset = self.get_bw_params()
 			seed_len,d.hash_preset = self.get_bw_params()
 		else:
 		else:
 			self._get_hash_preset()
 			self._get_hash_preset()
 			seed_len = opt.seed_len
 			seed_len = opt.seed_len
-		vmsg_r("Hashing brainwallet data.  Please wait...")
+		vmsg_r('Hashing brainwallet data.  Please wait...')
 		# Use buflen arg of scrypt.hash() to get seed of desired length
 		# Use buflen arg of scrypt.hash() to get seed of desired length
-		seed = scrypt_hash_passphrase(self.brainpasswd, "",
+		seed = scrypt_hash_passphrase(self.brainpasswd, '',
 					d.hash_preset, buflen=seed_len/8)
 					d.hash_preset, buflen=seed_len/8)
-		vmsg("Done")
+		vmsg('Done')
 		self.seed = Seed(seed)
 		self.seed = Seed(seed)
-		msg("Seed ID: %s" % self.seed.sid)
-		qmsg("Check this value against your records")
+		msg('Seed ID: %s' % self.seed.sid)
+		qmsg('Check this value against your records')
 		return True
 		return True
 
 
 
 
 class IncogWallet (SeedSourceEnc):
 class IncogWallet (SeedSourceEnc):
 
 
-	file_mode = "binary"
-	fmt_codes = "mmincog","incog","icg","i"
-	desc = "incognito data"
-	ext = "mmincog"
+	file_mode = 'binary'
+	fmt_codes = 'mmincog','incog','icg','i'
+	desc = 'incognito data'
+	ext = 'mmincog'
 	no_tty = True
 	no_tty = True
 
 
 	_msg = {
 	_msg = {
@@ -788,7 +754,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 	def _make_iv_chksum(self,s): return sha256(s).hexdigest()[:8].upper()
 	def _make_iv_chksum(self,s): return sha256(s).hexdigest()[:8].upper()
 
 
 	def _get_incog_data_len(self,seed_len):
 	def _get_incog_data_len(self,seed_len):
-		e = (g.hincog_chk_len,0)[int(bool(opt.old_incog_fmt))]
+		e = (g.hincog_chk_len,0)[bool(opt.old_incog_fmt)]
 		return g.aesctr_iv_len + g.salt_len + e + seed_len/8
 		return g.aesctr_iv_len + g.salt_len + e + seed_len/8
 
 
 	def _incog_data_size_chk(self):
 	def _incog_data_size_chk(self):
@@ -799,38 +765,38 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 			return True
 			return True
 		else:
 		else:
 			if opt.old_incog_fmt:
 			if opt.old_incog_fmt:
-				msg("WARNING: old-style incognito format requested.  " +
-					"Are you sure this is correct?")
-			msg(("Invalid incognito data size (%s bytes) for this " +
-				"seed length (%s bits)") % (dlen,opt.seed_len))
-			msg("Valid data size for this seed length: %s bytes" % valid_dlen)
+				msg('WARNING: old-style incognito format requested.  ' +
+					'Are you sure this is correct?')
+			msg(('Invalid incognito data size (%s bytes) for this ' +
+				'seed length (%s bits)') % (dlen,opt.seed_len))
+			msg('Valid data size for this seed length: %s bytes' % valid_dlen)
 			for sl in g.seed_lens:
 			for sl in g.seed_lens:
 				if dlen == self._get_incog_data_len(sl):
 				if dlen == self._get_incog_data_len(sl):
-					die(1,"Valid seed length for this data size: %s bits" % sl)
-			msg(("This data size (%s bytes) is invalid for all available " +
-				"seed lengths") % dlen)
+					die(1,'Valid seed length for this data size: %s bits' % sl)
+			msg(('This data size (%s bytes) is invalid for all available ' +
+				'seed lengths') % dlen)
 			return False
 			return False
 
 
 	def _encrypt (self):
 	def _encrypt (self):
 		self._get_first_pw_and_hp_and_encrypt_seed()
 		self._get_first_pw_and_hp_and_encrypt_seed()
 		if opt.old_incog_fmt:
 		if opt.old_incog_fmt:
-			die(1,"Writing old-format incog wallets is unsupported")
+			die(1,'Writing old-format incog wallets is unsupported')
 		d = self.ssdata
 		d = self.ssdata
 		# IV is used BOTH to initialize counter and to salt password!
 		# IV is used BOTH to initialize counter and to salt password!
 		d.iv = get_random(g.aesctr_iv_len)
 		d.iv = get_random(g.aesctr_iv_len)
 		d.iv_id = self._make_iv_chksum(d.iv)
 		d.iv_id = self._make_iv_chksum(d.iv)
-		msg("New Incog Wallet ID: %s" % d.iv_id)
-		qmsg("Make a record of this value")
+		msg('New Incog Wallet ID: %s' % d.iv_id)
+		qmsg('Make a record of this value')
 		vmsg(self.msg['record_incog_id'])
 		vmsg(self.msg['record_incog_id'])
 
 
 		d.salt = get_random(g.salt_len)
 		d.salt = get_random(g.salt_len)
-		key = make_key(d.passwd, d.salt, d.hash_preset, "incog wallet key")
+		key = make_key(d.passwd, d.salt, d.hash_preset, 'incog wallet key')
 		chk = sha256(self.seed.data).digest()[:8]
 		chk = sha256(self.seed.data).digest()[:8]
-		d.enc_seed = encrypt_data(chk + self.seed.data, key, 1, "seed")
+		d.enc_seed = encrypt_data(chk + self.seed.data, key, 1, 'seed')
 
 
-		d.wrapper_key = make_key(d.passwd, d.iv, d.hash_preset, "incog wrapper key")
+		d.wrapper_key = make_key(d.passwd, d.iv, d.hash_preset, 'incog wrapper key')
 		d.key_id = make_chksum_8(d.wrapper_key)
 		d.key_id = make_chksum_8(d.wrapper_key)
-		vmsg("Key ID: %s" % d.key_id)
+		vmsg('Key ID: %s' % d.key_id)
 		d.target_data_len = self._get_incog_data_len(self.seed.length)
 		d.target_data_len = self._get_incog_data_len(self.seed.length)
 
 
 	def _format(self):
 	def _format(self):
@@ -845,7 +811,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 	def _filename(self):
 	def _filename(self):
 		s = self.seed
 		s = self.seed
 		d = self.ssdata
 		d = self.ssdata
-		return "{}-{}-{}[{},{}].{}".format(
+		return '{}-{}-{}[{},{}].{}'.format(
 				s.sid,
 				s.sid,
 				d.key_id,
 				d.key_id,
 				d.iv_id,
 				d.iv_id,
@@ -862,8 +828,8 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 		d.iv             = self.fmt_data[0:g.aesctr_iv_len]
 		d.iv             = self.fmt_data[0:g.aesctr_iv_len]
 		d.incog_id       = self._make_iv_chksum(d.iv)
 		d.incog_id       = self._make_iv_chksum(d.iv)
 		d.enc_incog_data = self.fmt_data[g.aesctr_iv_len:]
 		d.enc_incog_data = self.fmt_data[g.aesctr_iv_len:]
-		msg("Incog Wallet ID: %s" % d.incog_id)
-		qmsg("Check this value against your records")
+		msg('Incog Wallet ID: %s' % d.incog_id)
+		qmsg('Check this value against your records')
 		vmsg(self.msg['check_incog_id'])
 		vmsg(self.msg['check_incog_id'])
 
 
 		return True
 		return True
@@ -871,14 +837,14 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 	def _verify_seed_newfmt(self,data):
 	def _verify_seed_newfmt(self,data):
 		chk,seed = data[:8],data[8:]
 		chk,seed = data[:8],data[8:]
 		if sha256(seed).digest()[:8] == chk:
 		if sha256(seed).digest()[:8] == chk:
-			qmsg("Passphrase%s are correct" % (self.msg['dec_chk'] % "and"))
+			qmsg('Passphrase%s are correct' % (self.msg['dec_chk'] % 'and'))
 			return seed
 			return seed
 		else:
 		else:
-			msg("Incorrect passphrase%s" % (self.msg['dec_chk'] % "or"))
+			msg('Incorrect passphrase%s' % (self.msg['dec_chk'] % 'or'))
 			return False
 			return False
 
 
 	def _verify_seed_oldfmt(self,seed):
 	def _verify_seed_oldfmt(self,seed):
-		m = "Seed ID: %s.  Is the Seed ID correct?" % make_chksum_8(seed)
+		m = 'Seed ID: %s.  Is the Seed ID correct?' % make_chksum_8(seed)
 		if keypress_confirm(m, True):
 		if keypress_confirm(m, True):
 			return seed
 			return seed
 		else:
 		else:
@@ -890,24 +856,24 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 		self._get_passphrase(desc_suf=d.incog_id)
 		self._get_passphrase(desc_suf=d.incog_id)
 
 
 		# IV is used BOTH to initialize counter and to salt password!
 		# IV is used BOTH to initialize counter and to salt password!
-		key = make_key(d.passwd, d.iv, d.hash_preset, "wrapper key")
+		key = make_key(d.passwd, d.iv, d.hash_preset, 'wrapper key')
 		dd = decrypt_data(d.enc_incog_data, key,
 		dd = decrypt_data(d.enc_incog_data, key,
-				int(hexlify(d.iv),16), "incog data")
+				int(hexlify(d.iv),16), 'incog data')
 
 
 		d.salt     = dd[0:g.salt_len]
 		d.salt     = dd[0:g.salt_len]
 		d.enc_seed = dd[g.salt_len:]
 		d.enc_seed = dd[g.salt_len:]
 
 
-		key = make_key(d.passwd, d.salt, d.hash_preset, "main key")
-		qmsg("Key ID: %s" % make_chksum_8(key))
+		key = make_key(d.passwd, d.salt, d.hash_preset, 'main key')
+		qmsg('Key ID: %s' % make_chksum_8(key))
 
 
-		verify_seed = getattr(self,"_verify_seed_"+
-						("newfmt","oldfmt")[int(bool(opt.old_incog_fmt))])
+		verify_seed = getattr(self,'_verify_seed_'+
+						('newfmt','oldfmt')[bool(opt.old_incog_fmt)])
 
 
-		seed = verify_seed(decrypt_seed(d.enc_seed, key, "", ""))
+		seed = verify_seed(decrypt_seed(d.enc_seed, key, '', ''))
 
 
 		if seed:
 		if seed:
 			self.seed = Seed(seed)
 			self.seed = Seed(seed)
-			msg("Seed ID: %s" % self.seed.sid)
+			msg('Seed ID: %s' % self.seed.sid)
 			return True
 			return True
 		else:
 		else:
 			return False
 			return False
@@ -915,10 +881,10 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 
 
 class IncogWalletHex (IncogWallet):
 class IncogWalletHex (IncogWallet):
 
 
-	file_mode = "text"
-	desc = "hex incognito data"
-	fmt_codes = "mmincox","incox","incog_hex","xincog","ix","xi"
-	ext = "mmincox"
+	file_mode = 'text'
+	desc = 'hex incognito data'
+	fmt_codes = 'mmincox','incox','incog_hex','xincog','ix','xi'
+	ext = 'mmincox'
 	no_tty = False
 	no_tty = False
 
 
 	def _deformat(self):
 	def _deformat(self):
@@ -936,8 +902,8 @@ class IncogWalletHex (IncogWallet):
 
 
 class IncogWalletHidden (IncogWallet):
 class IncogWalletHidden (IncogWallet):
 
 
-	desc = "hidden incognito data"
-	fmt_codes = "incog_hidden","hincog","ih","hi"
+	desc = 'hidden incognito data'
+	fmt_codes = 'incog_hidden','hincog','ih','hi'
 	ext = None
 	ext = None
 
 
 	_msg = {
 	_msg = {
@@ -957,18 +923,18 @@ harder to find, you're advised to choose a much larger file size than this.
   identify the incog wallet data in the future and to locate the offset
   identify the incog wallet data in the future and to locate the offset
   where the data is hidden in the event you forget it.
   where the data is hidden in the event you forget it.
 	""",
 	""",
-		'dec_chk': ", hash preset, offset %s seed length"
+		'dec_chk': ', hash preset, offset %s seed length'
 	}
 	}
 
 
 
 
 	def _get_hincog_params(self,wtype):
 	def _get_hincog_params(self,wtype):
 		p = getattr(opt,'hidden_incog_'+ wtype +'_params')
 		p = getattr(opt,'hidden_incog_'+ wtype +'_params')
-		a,b = p.split(",")
+		a,b = p.split(',')
 		return a,int(b)
 		return a,int(b)
 
 
 	def _check_valid_offset(self,fn,action):
 	def _check_valid_offset(self,fn,action):
 		d = self.ssdata
 		d = self.ssdata
-		m = ("Input","Destination")[int(action=="write")]
+		m = ('Input','Destination')[action=='write']
 		if fn.size < d.hincog_offset + d.target_data_len:
 		if fn.size < d.hincog_offset + d.target_data_len:
 			die(1,
 			die(1,
 	"%s file '%s' has length %s, too short to %s %s bytes of data at offset %s"
 	"%s file '%s' has length %s, too short to %s %s bytes of data at offset %s"
@@ -976,29 +942,29 @@ harder to find, you're advised to choose a much larger file size than this.
 
 
 	def _get_data(self):
 	def _get_data(self):
 		d = self.ssdata
 		d = self.ssdata
-		d.hincog_offset = self._get_hincog_params("input")[1]
+		d.hincog_offset = self._get_hincog_params('input')[1]
 
 
 		qmsg("Getting hidden incog data from file '%s'" % self.infile.name)
 		qmsg("Getting hidden incog data from file '%s'" % self.infile.name)
 
 
 		# Already sanity-checked:
 		# Already sanity-checked:
 		d.target_data_len = self._get_incog_data_len(opt.seed_len)
 		d.target_data_len = self._get_incog_data_len(opt.seed_len)
-		self._check_valid_offset(self.infile,"read")
+		self._check_valid_offset(self.infile,'read')
 
 
 		fh = os.open(self.infile.name,os.O_RDONLY)
 		fh = os.open(self.infile.name,os.O_RDONLY)
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		os.close(fh)
 		os.close(fh)
 		qmsg("Data read from file '%s' at offset %s" %
 		qmsg("Data read from file '%s' at offset %s" %
-				(self.infile.name,d.hincog_offset), "Data read from file")
+				(self.infile.name,d.hincog_offset), 'Data read from file')
 
 
 	# overrides method in SeedSource
 	# overrides method in SeedSource
 	def write_to_file(self):
 	def write_to_file(self):
 		d = self.ssdata
 		d = self.ssdata
 		self._format()
 		self._format()
-		compare_or_die(d.target_data_len, "target data length",
-				len(self.fmt_data),"length of formatted " + self.desc)
+		compare_or_die(d.target_data_len, 'target data length',
+				len(self.fmt_data),'length of formatted ' + self.desc)
 
 
-		k = ("output","input")[int(self.op=="pwchg_new")]
+		k = ('output','input')[self.op=='pwchg_new']
 		fn,d.hincog_offset = self._get_hincog_params(k)
 		fn,d.hincog_offset = self._get_hincog_params(k)
 
 
 		check_offset = True
 		check_offset = True
@@ -1010,25 +976,25 @@ harder to find, you're advised to choose a much larger file size than this.
 				min_fsize = d.target_data_len + d.hincog_offset
 				min_fsize = d.target_data_len + d.hincog_offset
 				msg(self.msg['choose_file_size'].format(min_fsize))
 				msg(self.msg['choose_file_size'].format(min_fsize))
 				while True:
 				while True:
-					fsize = parse_nbytes(my_raw_input("Enter file size: "))
+					fsize = parse_nbytes(my_raw_input('Enter file size: '))
 					if fsize >= min_fsize: break
 					if fsize >= min_fsize: break
-					msg("File size must be an integer no less than %s" %
+					msg('File size must be an integer no less than %s' %
 							min_fsize)
 							min_fsize)
 
 
 				from mmgen.tool import rand2file
 				from mmgen.tool import rand2file
 				rand2file(fn, str(fsize))
 				rand2file(fn, str(fsize))
 				check_offset = False
 				check_offset = False
 			else:
 			else:
-				die(1,"Exiting at user request")
+				die(1,'Exiting at user request')
 
 
 		self.outfile = f = Filename(fn,ftype=self.fmt_codes[0],write=True)
 		self.outfile = f = Filename(fn,ftype=self.fmt_codes[0],write=True)
 
 
-		dmsg("%s data len %s, offset %s" % (
+		dmsg('%s data len %s, offset %s' % (
 				capfirst(self.desc),d.target_data_len,d.hincog_offset))
 				capfirst(self.desc),d.target_data_len,d.hincog_offset))
 
 
 		if check_offset:
 		if check_offset:
-			self._check_valid_offset(f,"write")
-			if not opt.quiet: confirm_or_exit("","alter file '%s'" % f.name)
+			self._check_valid_offset(f,'write')
+			if not opt.quiet: confirm_or_exit('',"alter file '%s'" % f.name)
 
 
 		fh = os.open(f.name,os.O_RDWR)
 		fh = os.open(f.name,os.O_RDWR)
 		os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
 		os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)

+ 36 - 32
mmgen/share/Opts.py

@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
-# Opts.py, an options parsing library for Python.  Copyright (C) 2014 by
+# Opts.py, an options parsing library for Python.  Copyright (C) 2014-2016
 # Philemon <mmgen-py@yandex.com>.
 # Philemon <mmgen-py@yandex.com>.
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
@@ -16,28 +16,32 @@
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
+"""
+Opts.py:  Generic options handling
+"""
+
 import sys, getopt
 import sys, getopt
 
 
 def usage(opts_data):
 def usage(opts_data):
-	print "USAGE: %s %s" % (opts_data['prog_name'], opts_data['usage'])
+	print 'USAGE: %s %s' % (opts_data['prog_name'], opts_data['usage'])
 	sys.exit(2)
 	sys.exit(2)
 
 
 def print_help(opts_data):
 def print_help(opts_data):
 	pn = opts_data['prog_name']
 	pn = opts_data['prog_name']
 	pn_len = str(len(pn)+2)
 	pn_len = str(len(pn)+2)
-	print ("  %-"+pn_len+"s %s") % (pn.upper()+":", opts_data['desc'].strip())
-	print ("  %-"+pn_len+"s %s %s")%("USAGE:", pn, opts_data['usage'].strip())
-	sep = "\n    "
-	print "  OPTIONS:"+sep+"%s" % sep.join(opts_data['options'].strip().splitlines())
-	if "notes" in opts_data:
-		print "  %s" % "\n  ".join(opts_data['notes'][1:-1].splitlines())
+	print ('  %-'+pn_len+'s %s') % (pn.upper()+':', opts_data['desc'].strip())
+	print ('  %-'+pn_len+'s %s %s')%('USAGE:', pn, opts_data['usage'].strip())
+	sep = '\n    '
+	print '  OPTIONS:' + sep + sep.join(opts_data['options'].strip().splitlines())
+	if 'notes' in opts_data:
+		print '  ' + '\n  '.join(opts_data['notes'][1:-1].splitlines())
 
 
 
 
 def process_opts(argv,opts_data,short_opts,long_opts):
 def process_opts(argv,opts_data,short_opts,long_opts):
 
 
 	import os
 	import os
 	opts_data['prog_name'] = os.path.basename(sys.argv[0])
 	opts_data['prog_name'] = os.path.basename(sys.argv[0])
-	long_opts  = [i.replace("_","-") for i in long_opts]
+	long_opts  = [i.replace('_','-') for i in long_opts]
 
 
 	try: cl_opts,args = getopt.getopt(argv[1:], short_opts, long_opts)
 	try: cl_opts,args = getopt.getopt(argv[1:], short_opts, long_opts)
 	except getopt.GetoptError as err:
 	except getopt.GetoptError as err:
@@ -46,21 +50,21 @@ def process_opts(argv,opts_data,short_opts,long_opts):
 	opts,short_opts_l = {},[]
 	opts,short_opts_l = {},[]
 
 
 	for i in short_opts:
 	for i in short_opts:
-		if i == ":": short_opts_l[-1] += i
+		if i == ':': short_opts_l[-1] += i
 		else:        short_opts_l     += i
 		else:        short_opts_l     += i
 
 
 	for opt, arg in cl_opts:
 	for opt, arg in cl_opts:
-		if   opt in ("-h","--help"): print_help(opts_data); sys.exit()
-		elif opt[:2] == "--" and opt[2:] in long_opts:
-			opts[opt[2:].replace("-","_")] = True
-		elif opt[:2] == "--" and opt[2:]+"=" in long_opts:
-			opts[opt[2:].replace("-","_")] = arg
-		elif opt[0] == "-" and opt[1]     in short_opts_l:
-			opts[long_opts[short_opts_l.index(opt[1:])].replace("-","_")] = True
-		elif opt[0] == "-" and opt[1:]+":" in short_opts_l:
+		if   opt in ('-h','--help'): print_help(opts_data); sys.exit()
+		elif opt[:2] == '--' and opt[2:] in long_opts:
+			opts[opt[2:].replace('-','_')] = True
+		elif opt[:2] == '--' and opt[2:]+'=' in long_opts:
+			opts[opt[2:].replace('-','_')] = arg
+		elif opt[0] == '-' and opt[1]     in short_opts_l:
+			opts[long_opts[short_opts_l.index(opt[1:])].replace('-','_')] = True
+		elif opt[0] == '-' and opt[1:]+':' in short_opts_l:
 			opts[long_opts[short_opts_l.index(
 			opts[long_opts[short_opts_l.index(
-					opt[1:]+":")][:-1].replace("-","_")] = arg
-		else: assert False, "Invalid option"
+					opt[1:]+':')][:-1].replace('-','_')] = arg
+		else: assert False, 'Invalid option'
 
 
 	if 'sets' in opts_data:
 	if 'sets' in opts_data:
 		for o_in,v_in,o_out,v_out in opts_data['sets']:
 		for o_in,v_in,o_out,v_out in opts_data['sets']:
@@ -69,9 +73,9 @@ def process_opts(argv,opts_data,short_opts,long_opts):
 				if (v and v_in == bool) or v == v_in:
 				if (v and v_in == bool) or v == v_in:
 					if o_out in opts and opts[o_out] != v_out:
 					if o_out in opts and opts[o_out] != v_out:
 						sys.stderr.write(
 						sys.stderr.write(
-				"Option conflict:\n  --%s=%s, with\n  --%s=%s\n" % (
-					o_out.replace("_","-"),opts[o_out],
-					o_in.replace("_","-"),opts[o_in]
+				'Option conflict:\n  --%s=%s, with\n  --%s=%s\n' % (
+					o_out.replace('_','-'),opts[o_out],
+					o_in.replace('_','-'),opts[o_in]
 				))
 				))
 						sys.exit(1)
 						sys.exit(1)
 					else:
 					else:
@@ -83,24 +87,24 @@ def process_opts(argv,opts_data,short_opts,long_opts):
 def parse_opts(argv,opts_data,opt_filter=None):
 def parse_opts(argv,opts_data,opt_filter=None):
 
 
 	import re
 	import re
-	pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{2,64})(=| )(.+)"
+	pat = r'^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{2,64})(=| )(.+)'
 	od,skip = [],True
 	od,skip = [],True
 
 
 	for l in opts_data['options'].strip().splitlines():
 	for l in opts_data['options'].strip().splitlines():
 		m = re.match(pat,l)
 		m = re.match(pat,l)
 		if m:
 		if m:
-			skip = True if (opt_filter and m.group(1) not in opt_filter) else False
-			app = [':','='] if (m.group(3) == '=') else ['','']
+			skip = (False,True)[bool(opt_filter) and m.group(1) not in opt_filter]
+			app = (['',''],[':','='])[m.group(3) == '=']
 			od.append(list(m.groups()) + app + [skip])
 			od.append(list(m.groups()) + app + [skip])
 		else:
 		else:
-			if not skip: od[-1][3] += "\n" + l
+			if not skip: od[-1][3] += '\n' + l
 
 
-	opts_data['options'] = "\n".join(
-		["-{}, --{} {}".format(d[0],d[1],d[3]) for d in od if d[6] == False]
+	opts_data['options'] = '\n'.join(
+		['-{}, --{} {}'.format(d[0],d[1],d[3]) for d in od if d[6] == False]
 	)
 	)
-	short_opts    = "".join([d[0]+d[4] for d in od if d[6] == False])
-	long_opts     = [d[1].replace("-","_")+d[5] for d in od if d[6] == False]
-	skipped_opts  = [d[1].replace("-","_") for d in od if d[6] == True]
+	short_opts    = ''.join([d[0]+d[4] for d in od if d[6] == False])
+	long_opts     = [d[1].replace('-','_')+d[5] for d in od if d[6] == False]
+	skipped_opts  = [d[1].replace('-','_') for d in od if d[6] == True]
 
 
 	opts,args = process_opts(argv,opts_data,short_opts,long_opts)
 	opts,args = process_opts(argv,opts_data,short_opts,long_opts)
 
 

+ 1 - 1
mmgen/share/__init__.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C) 2013-2015 by philemon <mmgen-py@yandex.com>
+# Copyright (C) 2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by

+ 29 - 30
mmgen/term.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,10 +20,9 @@
 term.py:  Terminal-handling routines for the MMGen suite
 term.py:  Terminal-handling routines for the MMGen suite
 """
 """
 
 
-import sys, os, struct
-import mmgen.globalvars as g
-import opt
-from mmgen.util import msg, msg_r
+import os,struct
+
+from mmgen.common import *
 
 
 def _kb_hold_protect_unix():
 def _kb_hold_protect_unix():
 
 
@@ -42,7 +41,7 @@ def _kb_hold_protect_unix():
 
 
 def _kb_hold_protect_unix_raw(): pass
 def _kb_hold_protect_unix_raw(): pass
 
 
-def _get_keypress_unix(prompt="",immed_chars="",prehold_protect=True):
+def _get_keypress_unix(prompt='',immed_chars='',prehold_protect=True):
 
 
 	msg_r(prompt)
 	msg_r(prompt)
 	timeout = float(0.3)
 	timeout = float(0.3)
@@ -57,8 +56,8 @@ def _get_keypress_unix(prompt="",immed_chars="",prehold_protect=True):
 		ch = sys.stdin.read(1)
 		ch = sys.stdin.read(1)
 		if prehold_protect:
 		if prehold_protect:
 			if key: continue
 			if key: continue
-		if immed_chars == "ALL" or ch in immed_chars: break
-		if immed_chars == "ALL_EXCEPT_ENTER" and not ch in "\n\r": break
+		if immed_chars == 'ALL' or ch in immed_chars: break
+		if immed_chars == 'ALL_EXCEPT_ENTER' and not ch in '\n\r': break
 		# Protect against long keypress
 		# Protect against long keypress
 		key = select([sys.stdin], [], [], timeout)[0]
 		key = select([sys.stdin], [], [], timeout)[0]
 		if not key: break
 		if not key: break
@@ -67,7 +66,7 @@ def _get_keypress_unix(prompt="",immed_chars="",prehold_protect=True):
 	return ch
 	return ch
 
 
 
 
-def _get_keypress_unix_raw(prompt="",immed_chars="",prehold_protect=None):
+def _get_keypress_unix_raw(prompt='',immed_chars='',prehold_protect=None):
 
 
 	msg_r(prompt)
 	msg_r(prompt)
 
 
@@ -98,7 +97,7 @@ def _kb_hold_protect_mswin():
 
 
 def _kb_hold_protect_mswin_raw(): pass
 def _kb_hold_protect_mswin_raw(): pass
 
 
-def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
+def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True):
 
 
 	msg_r(prompt)
 	msg_r(prompt)
 	timeout = float(0.5)
 	timeout = float(0.5)
@@ -109,9 +108,9 @@ def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
 
 
 			if ord(ch) == 3: raise KeyboardInterrupt
 			if ord(ch) == 3: raise KeyboardInterrupt
 
 
-			if immed_chars == "ALL" or ch in immed_chars:
+			if immed_chars == 'ALL' or ch in immed_chars:
 				return ch
 				return ch
-			if immed_chars == "ALL_EXCEPT_ENTER" and not ch in "\n\r":
+			if immed_chars == 'ALL_EXCEPT_ENTER' and not ch in '\n\r':
 				return ch
 				return ch
 
 
 			hit_time = time.time()
 			hit_time = time.time()
@@ -121,7 +120,7 @@ def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
 				if float(time.time() - hit_time) > timeout:
 				if float(time.time() - hit_time) > timeout:
 					return ch
 					return ch
 
 
-def _get_keypress_mswin_raw(prompt="",immed_chars="",prehold_protect=None):
+def _get_keypress_mswin_raw(prompt='',immed_chars='',prehold_protect=None):
 
 
 	msg_r(prompt)
 	msg_r(prompt)
 	ch = msvcrt.getch()
 	ch = msvcrt.getch()
@@ -160,7 +159,7 @@ def _get_terminal_size_linux():
 
 
 def _get_terminal_size_mswin():
 def _get_terminal_size_mswin():
 	try:
 	try:
-		from ctypes import windll, create_string_buffer
+		from ctypes import windll,create_string_buffer
 		# stdin handle is -10
 		# stdin handle is -10
 		# stdout handle is -11
 		# stdout handle is -11
 		# stderr handle is -12
 		# stderr handle is -12
@@ -169,7 +168,7 @@ def _get_terminal_size_mswin():
 		res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
 		res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
 		if res:
 		if res:
 			(bufx, bufy, curx, cury, wattr, left, top, right, bottom,
 			(bufx, bufy, curx, cury, wattr, left, top, right, bottom,
-			maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
+			maxx, maxy) = struct.unpack('hhhhHhhhhhh', csbi.raw)
 			sizex = right - left + 1
 			sizex = right - left + 1
 			sizey = bottom - top + 1
 			sizey = bottom - top + 1
 			return sizex, sizey
 			return sizex, sizey
@@ -179,7 +178,7 @@ def _get_terminal_size_mswin():
 def mswin_dummy_flush(fd,termconst): pass
 def mswin_dummy_flush(fd,termconst): pass
 
 
 try:
 try:
-	import tty, termios
+	import tty,termios
 	from select import select
 	from select import select
 	if g.disable_hold_protect:
 	if g.disable_hold_protect:
 		get_char = _get_keypress_unix_raw
 		get_char = _get_keypress_unix_raw
@@ -192,7 +191,7 @@ try:
 # call: myflush(sys.stdin, termios.TCIOFLUSH)
 # call: myflush(sys.stdin, termios.TCIOFLUSH)
 except:
 except:
 	try:
 	try:
-		import msvcrt, time
+		import msvcrt,time
 		if g.disable_hold_protect:
 		if g.disable_hold_protect:
 			get_char = _get_keypress_mswin_raw
 			get_char = _get_keypress_mswin_raw
 			kb_hold_protect = _kb_hold_protect_mswin_raw
 			kb_hold_protect = _kb_hold_protect_mswin_raw
@@ -202,18 +201,18 @@ except:
 		get_terminal_size = _get_terminal_size_mswin
 		get_terminal_size = _get_terminal_size_mswin
 		myflush = mswin_dummy_flush
 		myflush = mswin_dummy_flush
 	except:
 	except:
-		if not sys.platform.startswith("linux") \
-				and not sys.platform.startswith("win"):
-			msg("Unsupported platform: %s" % sys.platform)
-			msg("This program currently runs only on Linux and Windows")
+		if not sys.platform.startswith('linux') \
+				and not sys.platform.startswith('win'):
+			msg('Unsupported platform: %s' % sys.platform)
+			msg('This program currently runs only on Linux and Windows')
 		else:
 		else:
-			msg("Unable to set terminal mode")
+			msg('Unable to set terminal mode')
 		sys.exit(2)
 		sys.exit(2)
 
 
 
 
 def do_pager(text):
 def do_pager(text):
 
 
-	pagers = ["less","more"]
+	pagers = ['less','more']
 	shell = False
 	shell = False
 
 
 	from os import environ
 	from os import environ
@@ -223,23 +222,23 @@ def do_pager(text):
 # not found.
 # not found.
 # When 'shell' is false, an exception is raised, invoking the fallback
 # When 'shell' is false, an exception is raised, invoking the fallback
 # 'print' instead of the pager.
 # 'print' instead of the pager.
-# We risk assuming that "more" will always be available on a stock
+# We risk assuming that 'more' will always be available on a stock
 # Windows installation.
 # Windows installation.
-	if sys.platform.startswith("win") and 'HOME' not in environ:
+	if sys.platform.startswith('win') and 'HOME' not in environ:
 		shell = True
 		shell = True
-		pagers = ["more"]
+		pagers = ['more']
 
 
 	if 'PAGER' in environ and environ['PAGER'] != pagers[0]:
 	if 'PAGER' in environ and environ['PAGER'] != pagers[0]:
 		pagers = [environ['PAGER']] + pagers
 		pagers = [environ['PAGER']] + pagers
 
 
 	for pager in pagers:
 	for pager in pagers:
-		end = "" if pager == "less" else "\n(end of text)\n"
+		end = ('\n(end of text)\n','')[pager=='less']
 		try:
 		try:
-			from subprocess import Popen, PIPE, STDOUT
+			from subprocess import Popen,PIPE,STDOUT
 			p = Popen([pager], stdin=PIPE, shell=shell)
 			p = Popen([pager], stdin=PIPE, shell=shell)
 		except: pass
 		except: pass
 		else:
 		else:
-			p.communicate(text+end+"\n")
-			msg_r("\r")
+			p.communicate(text+end+'\n')
+			msg_r('\r')
 			break
 			break
 	else: Msg(text+end)
 	else: Msg(text+end)

+ 8 - 8
mmgen/test.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,10 +20,10 @@
 test.py:  Shared routines for the test suites
 test.py:  Shared routines for the test suites
 """
 """
 
 
-import sys,os
+import os
 from binascii import hexlify
 from binascii import hexlify
-from mmgen.util import msg,write_data_to_file,red,green
-import mmgen.opt as opt
+
+from mmgen.common import *
 
 
 def cleandir(d):
 def cleandir(d):
 	try:    files = os.listdir(d)
 	try:    files = os.listdir(d)
@@ -38,7 +38,7 @@ def getrandhex(n): return hexlify(os.urandom(n))
 def getrandstr(num_chars,no_space=False):
 def getrandstr(num_chars,no_space=False):
 	n,m = 95,32
 	n,m = 95,32
 	if no_space: n,m = 94,33
 	if no_space: n,m = 94,33
-	return "".join([chr(ord(i)%n+m) for i in list(os.urandom(num_chars))])
+	return ''.join([chr(ord(i)%n+m) for i in list(os.urandom(num_chars))])
 
 
 def mk_tmpdir(cfg):
 def mk_tmpdir(cfg):
 	try: os.mkdir(cfg['tmpdir'],0755)
 	try: os.mkdir(cfg['tmpdir'],0755)
@@ -66,8 +66,8 @@ def read_from_tmpfile(cfg,fn,binary=False):
 
 
 def ok():
 def ok():
 	if opt.verbose or opt.exact_output:
 	if opt.verbose or opt.exact_output:
-		sys.stderr.write(green("OK\n"))
-	else: msg(" OK")
+		sys.stderr.write(green('OK\n'))
+	else: msg(' OK')
 
 
 def ok_or_die(val,chk_func,s,skip_ok=False):
 def ok_or_die(val,chk_func,s,skip_ok=False):
 	try: ret = chk_func(val)
 	try: ret = chk_func(val)
@@ -83,6 +83,6 @@ def cmp_or_die(s,t,skip_ok=False):
 		if not skip_ok: ok()
 		if not skip_ok: ok()
 	else:
 	else:
 		sys.stderr.write(red(
 		sys.stderr.write(red(
-			"ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n" %
+			'ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n' %
 				(repr(t),repr(s))))
 				(repr(t),repr(s))))
 		sys.exit(3)
 		sys.exit(3)

+ 171 - 174
mmgen/tool.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,74 +20,71 @@
 tool.py:  Routines and data for the 'mmgen-tool' utility
 tool.py:  Routines and data for the 'mmgen-tool' utility
 """
 """
 
 
-import sys
-import mmgen.bitcoin as bitcoin
 import binascii as ba
 import binascii as ba
 
 
-import mmgen.globalvars as g
-import mmgen.opt as opt
+import mmgen.bitcoin as bitcoin
+from mmgen.common import *
 from mmgen.crypto import *
 from mmgen.crypto import *
-from mmgen.util import *
 from mmgen.tx import *
 from mmgen.tx import *
 
 
 pnm = g.proj_name
 pnm = g.proj_name
 
 
 from collections import OrderedDict
 from collections import OrderedDict
 cmd_data = OrderedDict([
 cmd_data = OrderedDict([
-	("help",         ['<tool command> [str]']),
-	("usage",        ['<tool command> [str]']),
-	("strtob58",     ['<string> [str]']),
-	("b58tostr",     ['<b58 number> [str]']),
-	("hextob58",     ['<hex number> [str]']),
-	("b58tohex",     ['<b58 number> [str]']),
-	("b58randenc",   []),
-	("b32tohex",     ['<b32 num> [str]']),
-	("hextob32",     ['<hex num> [str]']),
-	("randhex",      ['nbytes [int=32]']),
-	("id8",          ['<infile> [str]']),
-	("id6",          ['<infile> [str]']),
-	("sha256x2",     ['<str, hexstr or filename> [str]',
+	('help',         ['<tool command> [str]']),
+	('usage',        ['<tool command> [str]']),
+	('strtob58',     ['<string> [str]']),
+	('b58tostr',     ['<b58 number> [str]']),
+	('hextob58',     ['<hex number> [str]']),
+	('b58tohex',     ['<b58 number> [str]']),
+	('b58randenc',   []),
+	('b32tohex',     ['<b32 num> [str]']),
+	('hextob32',     ['<hex num> [str]']),
+	('randhex',      ['nbytes [int=32]']),
+	('id8',          ['<infile> [str]']),
+	('id6',          ['<infile> [str]']),
+	('sha256x2',     ['<str, hexstr or filename> [str]',
 							'hex_input [bool=False]','file_input [bool=False]']),
 							'hex_input [bool=False]','file_input [bool=False]']),
-	("str2id6",      ['<string (spaces are ignored)> [str]']),
-	("hexdump",      ['<infile> [str]', 'cols [int=8]', 'line_nums [bool=True]']),
-	("unhexdump",    ['<infile> [str]']),
-	("hexreverse",   ['<hexadecimal string> [str]']),
-	("hexlify",      ['<string> [str]']),
-	("rand2file",    ['<outfile> [str]','<nbytes> [str]','threads [int=4]','silent [bool=False']),
-
-	("randwif",    ['compressed [bool=False]']),
-	("randpair",   ['compressed [bool=False]']),
-	("hex2wif",    ['<private key in hex format> [str]', 'compressed [bool=False]']),
-	("wif2hex",    ['<wif> [str]', 'compressed [bool=False]']),
-	("wif2addr",   ['<wif> [str]', 'compressed [bool=False]']),
-	("hexaddr2addr", ['<btc address in hex format> [str]']),
-	("addr2hexaddr", ['<btc address> [str]']),
-	("pubkey2addr",  ['<public key in hex format> [str]']),
-	("pubkey2hexaddr", ['<public key in hex format> [str]']),
-	("privhex2addr", ['<private key in hex format> [str]','compressed [bool=False]']),
-
-	("hex2mn",       ['<hexadecimal string> [str]','wordlist [str="electrum"]']),
-	("mn2hex",       ['<mnemonic> [str]', 'wordlist [str="electrum"]']),
-	("mn_rand128",   ['wordlist [str="electrum"]']),
-	("mn_rand192",   ['wordlist [str="electrum"]']),
-	("mn_rand256",   ['wordlist [str="electrum"]']),
-	("mn_stats",     ['wordlist [str="electrum"]']),
-	("mn_printlist", ['wordlist [str="electrum"]']),
-
-
-	("listaddresses",['addrs [str='']','minconf [int=1]','showempty [bool=False]','pager [bool=False]','showbtcaddrs [bool=False]']),
-	("getbalance",   ['minconf [int=1]']),
-	("txview",       ['<{pnm} tx file> [str]','pager [bool=False]','terse [bool=False]'.format(pnm=pnm)]),
-
-	("add_label",       ['<{pnm} address> [str]'.format(pnm=pnm),'<label> [str]']),
-	("remove_label",    ['<{pnm} address> [str]'.format(pnm=pnm)]),
-	("addrfile_chksum", ['<{pnm} addr file> [str]'.format(pnm=pnm)]),
-	("keyaddrfile_chksum", ['<{pnm} addr file> [str]'.format(pnm=pnm)]),
-	("find_incog_data", ['<file or device name> [str]','<Incog ID> [str]','keep_searching [bool=False]']),
-
-	("encrypt",      ['<infile> [str]','outfile [str=""]','hash_preset [str=""]']),
-	("decrypt",      ['<infile> [str]','outfile [str=""]','hash_preset [str=""]']),
-	("bytespec",     ['<bytespec> [str]']),
+	('str2id6',      ['<string (spaces are ignored)> [str]']),
+	('hexdump',      ['<infile> [str]', 'cols [int=8]', 'line_nums [bool=True]']),
+	('unhexdump',    ['<infile> [str]']),
+	('hexreverse',   ['<hexadecimal string> [str]']),
+	('hexlify',      ['<string> [str]']),
+	('rand2file',    ['<outfile> [str]','<nbytes> [str]','threads [int=4]','silent [bool=False]']),
+
+	('randwif',    ['compressed [bool=False]']),
+	('randpair',   ['compressed [bool=False]']),
+	('hex2wif',    ['<private key in hex format> [str]', 'compressed [bool=False]']),
+	('wif2hex',    ['<wif> [str]', 'compressed [bool=False]']),
+	('wif2addr',   ['<wif> [str]', 'compressed [bool=False]']),
+	('hexaddr2addr', ['<btc address in hex format> [str]']),
+	('addr2hexaddr', ['<btc address> [str]']),
+	('pubkey2addr',  ['<public key in hex format> [str]']),
+	('pubkey2hexaddr', ['<public key in hex format> [str]']),
+	('privhex2addr', ['<private key in hex format> [str]','compressed [bool=False]']),
+
+	('hex2mn',       ['<hexadecimal string> [str]',"wordlist [str='electrum']"]),
+	('mn2hex',       ['<mnemonic> [str]', "wordlist [str='electrum']"]),
+	('mn_rand128',   ["wordlist [str='electrum']"]),
+	('mn_rand192',   ["wordlist [str='electrum']"]),
+	('mn_rand256',   ["wordlist [str='electrum']"]),
+	('mn_stats',     ["wordlist [str='electrum']"]),
+	('mn_printlist', ["wordlist [str='electrum']"]),
+
+
+	('listaddresses',["addrs [str='']",'minconf [int=1]','showempty [bool=False]','pager [bool=False]','showbtcaddrs [bool=False]']),
+	('getbalance',   ['minconf [int=1]']),
+	('txview',       ['<{} TX file> [str]'.format(pnm),'pager [bool=False]','terse [bool=False]']),
+
+	('add_label',       ['<{} address> [str]'.format(pnm),'<label> [str]']),
+	('remove_label',    ['<{} address> [str]'.format(pnm)]),
+	('addrfile_chksum', ['<{} addr file> [str]'.format(pnm)]),
+	('keyaddrfile_chksum', ['<{} addr file> [str]'.format(pnm)]),
+	('find_incog_data', ['<file or device name> [str]','<Incog ID> [str]','keep_searching [bool=False]']),
+
+	('encrypt',      ['<infile> [str]',"outfile [str='']","hash_preset [str='']"]),
+	('decrypt',      ['<infile> [str]',"outfile [str='']","hash_preset [str='']"]),
+	('bytespec',     ['<bytespec> [str]']),
 ])
 ])
 
 
 cmd_help = """
 cmd_help = """
@@ -144,7 +141,7 @@ cmd_help = """
   id8          - generate 8-character {pnm} ID for a file (or stdin)
   id8          - generate 8-character {pnm} ID for a file (or stdin)
   str2id6      - generate 6-character {pnm} ID for a string, ignoring spaces
   str2id6      - generate 6-character {pnm} ID for a string, ignoring spaces
 
 
-  Mnemonic operations (choose "electrum" (default), "tirosh" or "all"
+  Mnemonic operations (choose 'electrum' (default), 'tirosh' or 'all'
   wordlists):
   wordlists):
   mn_rand128   - generate random 128-bit mnemonic
   mn_rand128   - generate random 128-bit mnemonic
   mn_rand192   - generate random 192-bit mnemonic
   mn_rand192   - generate random 192-bit mnemonic
@@ -159,36 +156,38 @@ cmd_help = """
 """.format(pnm=pnm)
 """.format(pnm=pnm)
 
 
 def tool_usage(prog_name, command):
 def tool_usage(prog_name, command):
-	Msg("USAGE: '%s %s%s'" % (prog_name, command,
-		(" "+" ".join(cmd_data[command]) if cmd_data[command] else "")))
+	if command in cmd_data:
+		Msg('USAGE: %s %s %s' % (prog_name, command, ' '.join(cmd_data[command])))
+	else:
+		Msg("'%s': no such tool command" % command)
 
 
 def process_args(prog_name, command, cmd_args):
 def process_args(prog_name, command, cmd_args):
-	c_args = [[i.split(" [")[0],i.split(" [")[1][:-1]]
-		for i in cmd_data[command] if "=" not in i]
+	c_args = [[i.split(' [')[0],i.split(' [')[1][:-1]]
+		for i in cmd_data[command] if '=' not in i]
 	c_kwargs = dict([[
 	c_kwargs = dict([[
-			i.split(" [")[0],
-			[i.split(" [")[1].split("=")[0], i.split(" [")[1].split("=")[1][:-1]]
-		] for i in cmd_data[command] if "=" in i])
+			i.split(' [')[0],
+			[i.split(' [')[1].split('=')[0], i.split(' [')[1].split('=')[1][:-1]]
+		] for i in cmd_data[command] if '=' in i])
 
 
 	u_args = cmd_args[:len(c_args)]
 	u_args = cmd_args[:len(c_args)]
 	u_kwargs = cmd_args[len(c_args):]
 	u_kwargs = cmd_args[len(c_args):]
 
 
 	if len(u_args) < len(c_args):
 	if len(u_args) < len(c_args):
-		msg("%s arg%s required" % (len(c_args),suf(c_args,"k")))
+		msg('%s argument%s required' % (len(c_args),suf(c_args,'k')))
 		tool_usage(prog_name, command)
 		tool_usage(prog_name, command)
 		sys.exit(1)
 		sys.exit(1)
 
 
 	if len(u_kwargs) > len(c_kwargs):
 	if len(u_kwargs) > len(c_kwargs):
-		msg("Too many arguments")
+		msg('Too many arguments')
 		tool_usage(prog_name, command)
 		tool_usage(prog_name, command)
 		sys.exit(1)
 		sys.exit(1)
 
 
-	u_kwargs = dict([a.split("=") for a in u_kwargs])
+	u_kwargs = dict([a.split('=') for a in u_kwargs])
 
 
 #	print c_args; print c_kwargs; print u_args; print u_kwargs; sys.exit()
 #	print c_args; print c_kwargs; print u_args; print u_kwargs; sys.exit()
 
 
 	if set(u_kwargs) > set(c_kwargs):
 	if set(u_kwargs) > set(c_kwargs):
-		die(1,"Invalid named argument")
+		die(1,'Invalid named argument')
 
 
 	def convert_type(arg,arg_name,arg_type):
 	def convert_type(arg,arg_name,arg_type):
 		try:
 		try:
@@ -198,9 +197,9 @@ def process_args(prog_name, command, cmd_args):
 				(arg, arg_name, arg_type))
 				(arg, arg_name, arg_type))
 
 
 	def convert_to_bool_maybe(arg, arg_type):
 	def convert_to_bool_maybe(arg, arg_type):
-		if arg_type == "bool":
-			if arg.lower() in ("true","yes","1","on"): return True
-			if arg.lower() in ("false","no","0","off"): return False
+		if arg_type == 'bool':
+			if arg.lower() in ('true','yes','1','on'): return True
+			if arg.lower() in ('false','no','0','off'): return False
 		return arg
 		return arg
 
 
 	args = []
 	args = []
@@ -220,24 +219,24 @@ def process_args(prog_name, command, cmd_args):
 # Individual cmd_data
 # Individual cmd_data
 
 
 # def help():
 # def help():
-# 	Msg("Available commands:")
+# 	Msg('Available commands:')
 # 	for k in sorted(cmd_data.keys()):
 # 	for k in sorted(cmd_data.keys()):
-# 		Msg("%-16s %s" % (k," ".join(cmd_data[k])))
+# 		Msg('%-16s %s' % (k,' '.join(cmd_data[k])))
 
 
-def are_equal(a,b,dtype=""):
-	if dtype == "str": return a.lstrip("\0") == b.lstrip("\0")
-	if dtype == "hex": return a.lstrip("0") == b.lstrip("0")
-	if dtype == "b58": return a.lstrip("1") == b.lstrip("1")
+def are_equal(a,b,dtype=''):
+	if dtype == 'str': return a.lstrip('\0') == b.lstrip('\0')
+	if dtype == 'hex': return a.lstrip('0') == b.lstrip('0')
+	if dtype == 'b58': return a.lstrip('1') == b.lstrip('1')
 	else:              return a == b
 	else:              return a == b
 
 
 def print_convert_results(indata,enc,dec,dtype):
 def print_convert_results(indata,enc,dec,dtype):
 
 
-	error = False if are_equal(indata,dec,dtype) else True
+	error = (True,False)[are_equal(indata,dec,dtype)]
 
 
 	if error or opt.verbose:
 	if error or opt.verbose:
-		Msg("Input:         %s" % repr(indata))
-		Msg("Encoded data:  %s" % repr(enc))
-		Msg("Recoded data:  %s" % repr(dec))
+		Msg('Input:         %s' % repr(indata))
+		Msg('Encoded data:  %s' % repr(enc))
+		Msg('Recoded data:  %s' % repr(dec))
 	else: Msg(enc)
 	else: Msg(enc)
 
 
 	if error:
 	if error:
@@ -254,7 +253,7 @@ def hexdump(infile, cols=8, line_nums=True):
 				cols=cols,line_nums=line_nums))
 				cols=cols,line_nums=line_nums))
 
 
 def unhexdump(infile):
 def unhexdump(infile):
-	if sys.platform[:3] == "win":
+	if sys.platform[:3] == 'win':
 		import msvcrt
 		import msvcrt
 		msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
 		msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
 	sys.stdout.write(decode_pretty_hexdump(
 	sys.stdout.write(decode_pretty_hexdump(
@@ -263,31 +262,31 @@ def unhexdump(infile):
 def strtob58(s):
 def strtob58(s):
 	enc = bitcoin.b58encode(s)
 	enc = bitcoin.b58encode(s)
 	dec = bitcoin.b58decode(enc)
 	dec = bitcoin.b58decode(enc)
-	print_convert_results(s,enc,dec,"str")
+	print_convert_results(s,enc,dec,'str')
 
 
 def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode):
 def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode):
 	enc = f_enc(ba.unhexlify(s))
 	enc = f_enc(ba.unhexlify(s))
 	dec = ba.hexlify(f_dec(enc))
 	dec = ba.hexlify(f_dec(enc))
-	print_convert_results(s,enc,dec,"hex")
+	print_convert_results(s,enc,dec,'hex')
 
 
 def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
 def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
 	tmp = f_enc(s)
 	tmp = f_enc(s)
 	if tmp == False: sys.exit(1)
 	if tmp == False: sys.exit(1)
 	enc = ba.hexlify(tmp)
 	enc = ba.hexlify(tmp)
 	dec = f_dec(ba.unhexlify(enc))
 	dec = f_dec(ba.unhexlify(enc))
-	print_convert_results(s,enc,dec,"b58")
+	print_convert_results(s,enc,dec,'b58')
 
 
 def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
 def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
 	enc = f_enc(s)
 	enc = f_enc(s)
 	if enc == False: sys.exit(1)
 	if enc == False: sys.exit(1)
 	dec = f_dec(enc)
 	dec = f_dec(enc)
-	print_convert_results(s,enc,dec,"b58")
+	print_convert_results(s,enc,dec,'b58')
 
 
 def b58randenc():
 def b58randenc():
 	r = get_random(32)
 	r = get_random(32)
 	enc = bitcoin.b58encode(r)
 	enc = bitcoin.b58encode(r)
 	dec = bitcoin.b58decode(enc)
 	dec = bitcoin.b58decode(enc)
-	print_convert_results(r,enc,dec,"str")
+	print_convert_results(r,enc,dec,'str')
 
 
 def randhex(nbytes='32'):
 def randhex(nbytes='32'):
 	Msg(ba.hexlify(get_random(int(nbytes))))
 	Msg(ba.hexlify(get_random(int(nbytes))))
@@ -296,35 +295,35 @@ def randwif(compressed=False):
 	r_hex = ba.hexlify(get_random(32))
 	r_hex = ba.hexlify(get_random(32))
 	enc = bitcoin.hextowif(r_hex,compressed)
 	enc = bitcoin.hextowif(r_hex,compressed)
 	dec = bitcoin.wiftohex(enc,compressed)
 	dec = bitcoin.wiftohex(enc,compressed)
-	print_convert_results(r_hex,enc,dec,"hex")
+	print_convert_results(r_hex,enc,dec,'hex')
 
 
 def randpair(compressed=False):
 def randpair(compressed=False):
 	r_hex = ba.hexlify(get_random(32))
 	r_hex = ba.hexlify(get_random(32))
 	wif = bitcoin.hextowif(r_hex,compressed)
 	wif = bitcoin.hextowif(r_hex,compressed)
 	addr = bitcoin.privnum2addr(int(r_hex,16),compressed)
 	addr = bitcoin.privnum2addr(int(r_hex,16),compressed)
-	Vmsg("Key (hex):  %s" % r_hex)
-	Vmsg_r("Key (WIF):  "); Msg(wif)
-	Vmsg_r("Addr:       "); Msg(addr)
+	Vmsg('Key (hex):  %s' % r_hex)
+	Vmsg_r('Key (WIF):  '); Msg(wif)
+	Vmsg_r('Addr:       '); Msg(addr)
 
 
 def wif2addr(wif,compressed=False):
 def wif2addr(wif,compressed=False):
 	s_enc = bitcoin.wiftohex(wif,compressed)
 	s_enc = bitcoin.wiftohex(wif,compressed)
 	if s_enc == False:
 	if s_enc == False:
-		die(1,"Invalid address")
+		die(1,'Invalid address')
 	addr = bitcoin.privnum2addr(int(s_enc,16),compressed)
 	addr = bitcoin.privnum2addr(int(s_enc,16),compressed)
-	Vmsg_r("Addr: "); Msg(addr)
+	Vmsg_r('Addr: '); Msg(addr)
 
 
-wordlists = "electrum","tirosh"
-dfl_wordlist = "electrum"
+wordlists = 'electrum','tirosh'
+dfl_wordlist = 'electrum'
 
 
 from mmgen.seed import Mnemonic
 from mmgen.seed import Mnemonic
 def do_random_mn(nbytes,wordlist):
 def do_random_mn(nbytes,wordlist):
 	hexrand = ba.hexlify(get_random(nbytes))
 	hexrand = ba.hexlify(get_random(nbytes))
-	Vmsg("Seed: %s" % hexrand)
-	for wlname in (wordlists if wordlist == "all" else [wordlist]):
-		if wordlist == "all":
-			Msg("%s mnemonic:" % (wlname.capitalize()))
+	Vmsg('Seed: %s' % hexrand)
+	for wlname in ([wordlist],wordlists)[wordlist=='all']:
+		if wordlist == 'all':
+			Msg('%s mnemonic:' % (wlname.capitalize()))
 		mn = Mnemonic.hex2mn(hexrand,wordlist=wlname)
 		mn = Mnemonic.hex2mn(hexrand,wordlist=wlname)
-		Msg(" ".join(mn))
+		Msg(' '.join(mn))
 
 
 def mn_rand128(wordlist=dfl_wordlist): do_random_mn(16,wordlist)
 def mn_rand128(wordlist=dfl_wordlist): do_random_mn(16,wordlist)
 def mn_rand192(wordlist=dfl_wordlist): do_random_mn(24,wordlist)
 def mn_rand192(wordlist=dfl_wordlist): do_random_mn(24,wordlist)
@@ -332,26 +331,26 @@ def mn_rand256(wordlist=dfl_wordlist): do_random_mn(32,wordlist)
 
 
 def hex2mn(s,wordlist=dfl_wordlist):
 def hex2mn(s,wordlist=dfl_wordlist):
 	mn = Mnemonic.hex2mn(s,wordlist)
 	mn = Mnemonic.hex2mn(s,wordlist)
-	Msg(" ".join(mn))
+	Msg(' '.join(mn))
 
 
 def mn2hex(s,wordlist=dfl_wordlist):
 def mn2hex(s,wordlist=dfl_wordlist):
 	hexnum = Mnemonic.mn2hex(s.split(),wordlist)
 	hexnum = Mnemonic.mn2hex(s.split(),wordlist)
 	Msg(hexnum)
 	Msg(hexnum)
 
 
 def b32tohex(s):
 def b32tohex(s):
-	b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
+	b32a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
 	Msg(Mnemonic.baseNtohex(32,s,b32a))
 	Msg(Mnemonic.baseNtohex(32,s,b32a))
 
 
 def hextob32(s):
 def hextob32(s):
-	b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
-	Msg("".join(Mnemonic.hextobaseN(32,s,b32a)))
+	b32a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
+	Msg(''.join(Mnemonic.hextobaseN(32,s,b32a)))
 
 
 def mn_stats(wordlist=dfl_wordlist):
 def mn_stats(wordlist=dfl_wordlist):
 	Mnemonic.check_wordlist(wordlist)
 	Mnemonic.check_wordlist(wordlist)
 
 
 def mn_printlist(wordlist=dfl_wordlist):
 def mn_printlist(wordlist=dfl_wordlist):
 	wl = Mnemonic.get_wordlist(wordlist)
 	wl = Mnemonic.get_wordlist(wordlist)
-	Msg("\n".join(wl))
+	Msg('\n'.join(wl))
 
 
 def id8(infile):
 def id8(infile):
 	Msg(make_chksum_8(
 	Msg(make_chksum_8(
@@ -361,13 +360,11 @@ def id6(infile):
 	Msg(make_chksum_6(
 	Msg(make_chksum_6(
 		get_data_from_file(infile,dash=True,silent=True,binary=True)
 		get_data_from_file(infile,dash=True,silent=True,binary=True)
 	))
 	))
-def str2id6(s):  Msg(make_chksum_6("".join(s.split())))
+def str2id6(s):  Msg(make_chksum_6(''.join(s.split())))
 
 
 # List MMGen addresses and their balances:
 # List MMGen addresses and their balances:
 def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=False):
 def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=False):
 
 
-	from mmgen.tx import connect_to_bitcoind,trim_exponent,is_mmgen_addr,is_mmgen_seed_id
-
 	usr_addr_list = []
 	usr_addr_list = []
 	if addrs:
 	if addrs:
 		sid,idxs = split2(addrs,':')
 		sid,idxs = split2(addrs,':')
@@ -387,36 +384,37 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
 	from decimal import Decimal
 	from decimal import Decimal
 	total = Decimal('0')
 	total = Decimal('0')
 	for d in c.listunspent(0):
 	for d in c.listunspent(0):
-		mmaddr,comment = split2(d.account)
+		mmaddr,comment = split2(d['account'])
 		if usr_addr_list and (mmaddr not in usr_addr_list): continue
 		if usr_addr_list and (mmaddr not in usr_addr_list): continue
-		if is_mmgen_addr(mmaddr) and d.confirmations >= minconf:
+		if is_mmgen_addr(mmaddr) and d['confirmations'] >= minconf:
 			key = mmaddr.replace(':','_')
 			key = mmaddr.replace(':','_')
 			if key in addrs:
 			if key in addrs:
-				if addrs[key][2] != d.address:
+				if addrs[key][2] != d['address']:
 					die(2,'duplicate BTC address ({}) for this MMGen address! ({})'.format(
 					die(2,'duplicate BTC address ({}) for this MMGen address! ({})'.format(
-							(d.address, addrs[key][2])))
+							(d['address'], addrs[key][2])))
 			else:
 			else:
-				addrs[key] = [0,comment,d.address]
-			addrs[key][0] += d.amount
-			total += d.amount
+				addrs[key] = [0,comment,d['address']]
+			addrs[key][0] += d['amount']
+			total += d['amount']
 
 
 	# We use listaccounts only for empty addresses, as it shows false positive balances
 	# We use listaccounts only for empty addresses, as it shows false positive balances
 	if showempty:
 	if showempty:
-		accts = c.listaccounts(minconf=0,includeWatchonly=True,as_dict=True)
-		for a in accts:
-			mmaddr,comment = split2(a)
+		accts = c.listaccounts(0,True) # minconf,watchonly
+		save_a = []
+		for acct in accts:
+			mmaddr,comment = split2(acct)
 			if usr_addr_list and (mmaddr not in usr_addr_list): continue
 			if usr_addr_list and (mmaddr not in usr_addr_list): continue
 			if is_mmgen_addr(mmaddr):
 			if is_mmgen_addr(mmaddr):
 				key = mmaddr.replace(':','_')
 				key = mmaddr.replace(':','_')
 				if key not in addrs:
 				if key not in addrs:
-					if showbtcaddrs:
-						tmp = c.getaddressesbyaccount(a)
-						if len(tmp) != 1:
-							die(2,"Account '%s' has more or less than one BTC address!" % a)
-						baddr = tmp[0]
-					else:
-						baddr = ''
-					addrs[key] = [0,comment,baddr]
+					if showbtcaddrs: save_a.append([acct])
+					addrs[key] = [0,comment,'']
+
+		for acct,addr in zip(save_a,c.getaddressesbyaccount(save_a,batch=True)):
+			if len(addr) != 1:
+				die(2,"Account '%s' has more or less than one BTC address!" % addr)
+			key = split2(acct[0])[0].replace(':','_')
+			addrs[key][2] = addr[0]
 
 
 	if not addrs:
 	if not addrs:
 		die(1,('No addresses with balances!','No tracked addresses!')[showempty])
 		die(1,('No addresses with balances!','No tracked addresses!')[showempty])
@@ -424,7 +422,7 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
 	fs = '%-{}s %-{}s %-{}s %s'.format(
 	fs = '%-{}s %-{}s %-{}s %s'.format(
 		max(len(k) for k in addrs),
 		max(len(k) for k in addrs),
 		(0,36)[showbtcaddrs],
 		(0,36)[showbtcaddrs],
-		max(len(addrs[k][1]) for k in addrs) + 1
+		max(max(len(addrs[k][1]) for k in addrs) + 1,8) # pad 8 if no comments
 	)
 	)
 
 
 	def s_mmgen(key):
 	def s_mmgen(key):
@@ -444,39 +442,38 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
 
 
 
 
 def getbalance(minconf=1):
 def getbalance(minconf=1):
-	from mmgen.tx import connect_to_bitcoind,trim_exponent,is_mmgen_addr
 
 
 	accts = {}
 	accts = {}
 	for d in connect_to_bitcoind().listunspent(0):
 	for d in connect_to_bitcoind().listunspent(0):
-		ma = split2(d.account)[0]
-		keys = ["TOTAL"]
-		if d.spendable: keys += ["SPENDABLE"]
-		if is_mmgen_addr(ma): keys += [ma.split(":")[0]]
-		confs = d.confirmations
-		i = 2 if confs >= minconf else 1
+		ma = split2(d['account'])[0]
+		keys = ['TOTAL']
+		if d['spendable']: keys += ['SPENDABLE']
+		if is_mmgen_addr(ma): keys += [ma.split(':')[0]]
+		confs = d['confirmations']
+		i = (1,2)[confs >= minconf]
 
 
 		for key in keys:
 		for key in keys:
 			if key not in accts: accts[key] = [0,0,0]
 			if key not in accts: accts[key] = [0,0,0]
-			for j in ([0] if confs == 0 else []) + [i]:
-				accts[key][j] += d.amount
+			for j in ([],[0])[confs==0] + [i]:
+				accts[key][j] += d['amount']
 
 
-	fs = "{:12}  {:<%s} {:<%s} {:<}" % (16,16)
-	mc,lbl = str(minconf),"confirms"
-	Msg(fs.format("Wallet","Unconfirmed","<%s %s"%(mc,lbl),">=%s %s"%(mc,lbl)))
+	fs = '{:12}  {:<%s} {:<%s} {:<}' % (16,16)
+	mc,lbl = str(minconf),'confirms'
+	Msg(fs.format('Wallet','Unconfirmed','<%s %s'%(mc,lbl),'>=%s %s'%(mc,lbl)))
 	for key in sorted(accts.keys()):
 	for key in sorted(accts.keys()):
-		Msg(fs.format(key+":", *[str(trim_exponent(a))+" BTC"
+		Msg(fs.format(key+':', *[str(trim_exponent(a))+' BTC'
 				for a in accts[key]]))
 				for a in accts[key]]))
 
 
 def txview(infile,pager=False,terse=False):
 def txview(infile,pager=False,terse=False):
 	c = connect_to_bitcoind()
 	c = connect_to_bitcoind()
-	tx_data = get_lines_from_file(infile,"transaction data")
+	tx_data = get_lines_from_file(infile,'transaction data')
 
 
 	metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,infile)
 	metadata,tx_hex,inputs_data,b2m_map,comment = parse_tx_file(tx_data,infile)
 	view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pager,pause=False,terse=terse)
 	view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pager,pause=False,terse=terse)
 
 
 def add_label(mmaddr,label,remove=False):
 def add_label(mmaddr,label,remove=False):
 	if not is_mmgen_addr(mmaddr):
 	if not is_mmgen_addr(mmaddr):
-		die(1,"{a}: not a valid {pnm} address".format(pnm=pnm,a=mmaddr))
+		die(1,'{a}: not a valid {pnm} address'.format(pnm=pnm,a=mmaddr))
 	check_addr_label(label)  # Exits on failure
 	check_addr_label(label)  # Exits on failure
 
 
 	c = connect_to_bitcoind()
 	c = connect_to_bitcoind()
@@ -485,28 +482,28 @@ def add_label(mmaddr,label,remove=False):
 	btcaddr = AddrInfoList(bitcoind_connection=c).mmaddr2btcaddr(mmaddr)
 	btcaddr = AddrInfoList(bitcoind_connection=c).mmaddr2btcaddr(mmaddr)
 
 
 	if not btcaddr:
 	if not btcaddr:
-		die(1,"{pnm} address {a} not found in tracking wallet".format(
+		die(1,'{pnm} address {a} not found in tracking wallet'.format(
 				pnm=pnm,a=mmaddr))
 				pnm=pnm,a=mmaddr))
 
 
 	try:
 	try:
-		l = " " + label if label else ""
-		c.importaddress(btcaddr,mmaddr+l,rescan=False)
+		l = ' ' + label if label else ''
+		c.importaddress(btcaddr,mmaddr+l,False) # addr,label,rescan,p2sh
 	except:
 	except:
-		die(1,"Unable to add label")
+		die(1,'Unable to add label')
 
 
-	s = "{pnm} address {a} in tracking wallet".format(a=mmaddr,pnm=pnm)
-	if remove: msg("Removed label from {}".format(s))
+	s = '{pnm} address {a} in tracking wallet'.format(a=mmaddr,pnm=pnm)
+	if remove: msg('Removed label from {}'.format(s))
 	else:      msg("Added label '{}' for {}".format(label,s))
 	else:      msg("Added label '{}' for {}".format(label,s))
 
 
-def remove_label(mmaddr): add_label(mmaddr,"",remove=True)
+def remove_label(mmaddr): add_label(mmaddr,'',remove=True)
 
 
 def addrfile_chksum(infile):
 def addrfile_chksum(infile):
 	from mmgen.addr import AddrInfo
 	from mmgen.addr import AddrInfo
-	AddrInfo(infile)
+	AddrInfo(infile,caller='tool')
 
 
 def keyaddrfile_chksum(infile):
 def keyaddrfile_chksum(infile):
 	from mmgen.addr import AddrInfo
 	from mmgen.addr import AddrInfo
-	AddrInfo(infile,has_keys=True)
+	AddrInfo(infile,has_keys=True,caller='tool')
 
 
 def hexreverse(hex_str):
 def hexreverse(hex_str):
 	Msg(ba.hexlify(decode_pretty_hexdump(hex_str)[::-1]))
 	Msg(ba.hexlify(decode_pretty_hexdump(hex_str)[::-1]))
@@ -543,36 +540,36 @@ def hex2wif(hexpriv,compressed=False):
 	Msg(bitcoin.hextowif(hexpriv,compressed))
 	Msg(bitcoin.hextowif(hexpriv,compressed))
 
 
 
 
-def encrypt(infile,outfile="",hash_preset=""):
-	data = get_data_from_file(infile,"data for encryption",binary=True)
-	enc_d = mmgen_encrypt(data,"user data",hash_preset)
+def encrypt(infile,outfile='',hash_preset=''):
+	data = get_data_from_file(infile,'data for encryption',binary=True)
+	enc_d = mmgen_encrypt(data,'user data',hash_preset)
 	if not outfile:
 	if not outfile:
-		outfile = "%s.%s" % (os.path.basename(infile),g.mmenc_ext)
+		outfile = '%s.%s' % (os.path.basename(infile),g.mmenc_ext)
 
 
-	write_data_to_file(outfile,enc_d,"encrypted data",binary=True)
+	write_data_to_file(outfile,enc_d,'encrypted data',binary=True)
 
 
 
 
-def decrypt(infile,outfile="",hash_preset=""):
-	enc_d = get_data_from_file(infile,"encrypted data",binary=True)
+def decrypt(infile,outfile='',hash_preset=''):
+	enc_d = get_data_from_file(infile,'encrypted data',binary=True)
 	while True:
 	while True:
-		dec_d = mmgen_decrypt(enc_d,"user data",hash_preset)
+		dec_d = mmgen_decrypt(enc_d,'user data',hash_preset)
 		if dec_d: break
 		if dec_d: break
-		msg("Trying again...")
+		msg('Trying again...')
 
 
 	if not outfile:
 	if not outfile:
 		o = os.path.basename(infile)
 		o = os.path.basename(infile)
 		outfile = remove_extension(o,g.mmenc_ext)
 		outfile = remove_extension(o,g.mmenc_ext)
-		if outfile == o: outfile += ".dec"
+		if outfile == o: outfile += '.dec'
 
 
-	write_data_to_file(outfile,dec_d,"decrypted data",binary=True)
+	write_data_to_file(outfile,dec_d,'decrypted data',binary=True)
 
 
 
 
 def find_incog_data(filename,iv_id,keep_searching=False):
 def find_incog_data(filename,iv_id,keep_searching=False):
 	ivsize,bsize,mod = g.aesctr_iv_len,4096,4096*8
 	ivsize,bsize,mod = g.aesctr_iv_len,4096,4096*8
-	n,carry = 0," "*ivsize
+	n,carry = 0,' '*ivsize
 	f = os.open(filename,os.O_RDONLY)
 	f = os.open(filename,os.O_RDONLY)
 	for ch in iv_id:
 	for ch in iv_id:
-		if ch not in "0123456789ABCDEF":
+		if ch not in '0123456789ABCDEF':
 			die(2,"'%s': invalid Incog ID" % iv_id)
 			die(2,"'%s': invalid Incog ID" % iv_id)
 	while True:
 	while True:
 		d = os.read(f,bsize)
 		d = os.read(f,bsize)
@@ -581,14 +578,14 @@ def find_incog_data(filename,iv_id,keep_searching=False):
 		for i in range(bsize):
 		for i in range(bsize):
 			if sha256(d[i:i+ivsize]).hexdigest()[:8].upper() == iv_id:
 			if sha256(d[i:i+ivsize]).hexdigest()[:8].upper() == iv_id:
 				if n+i < ivsize: continue
 				if n+i < ivsize: continue
-				msg("\rIncog data for ID %s found at offset %s" %
+				msg('\rIncog data for ID %s found at offset %s' %
 					(iv_id,n+i-ivsize))
 					(iv_id,n+i-ivsize))
-				if not keep_searching: sys.exit(0)
+				if not keep_searching: sys.exit()
 		carry = d[len(d)-ivsize:]
 		carry = d[len(d)-ivsize:]
 		n += bsize
 		n += bsize
-		if not n % mod: msg_r("\rSearched: %s bytes" % n)
+		if not n % mod: msg_r('\rSearched: %s bytes' % n)
 
 
-	msg("")
+	msg('')
 	os.close(f)
 	os.close(f)
 
 
 
 
@@ -601,7 +598,7 @@ def rand2file(outfile, nbytes, threads=4, silent=False):
 	bsize = 2**20
 	bsize = 2**20
 	roll = bsize * 4
 	roll = bsize * 4
 	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
-	f = open(outfile,"wb")
+	f = open(outfile,'wb')
 
 
 	from Crypto.Cipher import AES
 	from Crypto.Cipher import AES
 	from Crypto.Util import Counter
 	from Crypto.Util import Counter
@@ -641,10 +638,10 @@ def rand2file(outfile, nbytes, threads=4, silent=False):
 		rbytes -= bsize
 		rbytes -= bsize
 		i += 1
 		i += 1
 		if not (bsize*i) % roll:
 		if not (bsize*i) % roll:
-			msg_r("\rRead: %s bytes" % (bsize*i))
+			msg_r('\rRead: %s bytes' % (bsize*i))
 
 
 	if not silent:
 	if not silent:
-		msg("\rRead: %s bytes" % nbytes)
+		msg('\rRead: %s bytes' % nbytes)
 		qmsg("\r%s bytes of random data written to file '%s'" % (nbytes,outfile))
 		qmsg("\r%s bytes of random data written to file '%s'" % (nbytes,outfile))
 	q1.join()
 	q1.join()
 	q2.join()
 	q2.join()

+ 99 - 102
mmgen/tx.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -21,13 +21,12 @@ tx.py:  Bitcoin transaction routines
 """
 """
 
 
 import sys, os
 import sys, os
+from stat import *
 from binascii import unhexlify
 from binascii import unhexlify
 from decimal import Decimal
 from decimal import Decimal
 from collections import OrderedDict
 from collections import OrderedDict
 
 
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import *
+from mmgen.common import *
 from mmgen.term import do_pager
 from mmgen.term import do_pager
 
 
 def trim_exponent(n):
 def trim_exponent(n):
@@ -43,30 +42,30 @@ def normalize_btc_amt(amt):
 	try:
 	try:
 		ret = Decimal(amt)
 		ret = Decimal(amt)
 	except:
 	except:
-		msg("%s: Invalid amount" % amt)
+		msg('%s: Invalid amount' % amt)
 		return False
 		return False
 
 
-	dmsg("Decimal(amt): %s\nAs tuple: %s" % (amt,repr(ret.as_tuple())))
+	dmsg('Decimal(amt): %s\nAs tuple: %s' % (amt,repr(ret.as_tuple())))
 
 
 	if ret.as_tuple()[-1] < -8:
 	if ret.as_tuple()[-1] < -8:
-		msg("%s: Too many decimal places in amount" % amt)
+		msg('%s: Too many decimal places in amount' % amt)
 		return False
 		return False
 
 
 	if ret == 0:
 	if ret == 0:
-		msg("Requested zero BTC amount")
+		msg('Requested zero BTC amount')
 		return False
 		return False
 
 
 	return trim_exponent(ret)
 	return trim_exponent(ret)
 
 
 def parse_mmgen_label(s,check_label_len=False):
 def parse_mmgen_label(s,check_label_len=False):
 	l = split2(s)
 	l = split2(s)
-	if not is_mmgen_addr(l[0]): return "",s
+	if not is_mmgen_addr(l[0]): return '',s
 	if check_label_len: check_addr_label(l[1])
 	if check_label_len: check_addr_label(l[1])
 	return tuple(l)
 	return tuple(l)
 
 
 def is_mmgen_seed_id(s):
 def is_mmgen_seed_id(s):
 	import re
 	import re
-	return re.match(r"^[0123456789ABCDEF]{8}$",s) is not None
+	return re.match(r'^[0123456789ABCDEF]{8}$',s) is not None
 
 
 def is_mmgen_idx(s):
 def is_mmgen_idx(s):
 	try: int(s)
 	try: int(s)
@@ -74,7 +73,7 @@ def is_mmgen_idx(s):
 	return len(s) <= g.mmgen_idx_max_digits
 	return len(s) <= g.mmgen_idx_max_digits
 
 
 def is_mmgen_addr(s):
 def is_mmgen_addr(s):
-	seed_id,idx = split2(s,":")
+	seed_id,idx = split2(s,':')
 	return is_mmgen_seed_id(seed_id) and is_mmgen_idx(idx)
 	return is_mmgen_seed_id(seed_id) and is_mmgen_idx(idx)
 
 
 def is_btc_addr(s):
 def is_btc_addr(s):
@@ -86,13 +85,13 @@ def is_b58_str(s):
 	return set(list(s)) <= set(b58a)
 	return set(list(s)) <= set(b58a)
 
 
 def is_wif(s):
 def is_wif(s):
-	if s == "": return False
+	if s == '': return False
 	compressed = not s[0] == '5'
 	compressed = not s[0] == '5'
 	from mmgen.bitcoin import wiftohex
 	from mmgen.bitcoin import wiftohex
 	return wiftohex(s,compressed) is not False
 	return wiftohex(s,compressed) is not False
 
 
 def wiftoaddr(s):
 def wiftoaddr(s):
-	if s == "": return False
+	if s == '': return False
 	compressed = not s[0] == '5'
 	compressed = not s[0] == '5'
 	from mmgen.bitcoin import wiftohex,privnum2addr
 	from mmgen.bitcoin import wiftohex,privnum2addr
 	hex_key = wiftohex(s,compressed)
 	hex_key = wiftohex(s,compressed)
@@ -102,13 +101,13 @@ def wiftoaddr(s):
 
 
 def is_valid_tx_comment(s):
 def is_valid_tx_comment(s):
 
 
-	try: s = s.decode("utf8")
+	try: s = s.decode('utf8')
 	except:
 	except:
-		msg("Invalid transaction comment (not UTF-8)")
+		msg('Invalid transaction comment (not UTF-8)')
 		return False
 		return False
 
 
 	if len(s) > g.max_tx_comment_len:
 	if len(s) > g.max_tx_comment_len:
-		msg("Invalid transaction comment (longer than %s characters)" %
+		msg('Invalid transaction comment (longer than %s characters)' %
 				g.max_tx_comment_len)
 				g.max_tx_comment_len)
 		return False
 		return False
 
 
@@ -125,35 +124,38 @@ def check_addr_label(label):
 	for ch in label:
 	for ch in label:
 		if ch not in g.addr_label_symbols:
 		if ch not in g.addr_label_symbols:
 			msg("""
 			msg("""
-"%s": illegal character in label "%s".
+'%s': illegal character in label '%s'.
 Only ASCII printable characters are permitted.
 Only ASCII printable characters are permitted.
 """.strip() % (ch,label))
 """.strip() % (ch,label))
 			sys.exit(3)
 			sys.exit(3)
 
 
 def prompt_and_view_tx_data(c,prompt,inputs_data,tx_hex,adata,comment,metadata):
 def prompt_and_view_tx_data(c,prompt,inputs_data,tx_hex,adata,comment,metadata):
 
 
-	prompt += " (y)es, (N)o, pager (v)iew, (t)erse view"
+	prompt += ' (y)es, (N)o, pager (v)iew, (t)erse view'
 
 
-	reply = prompt_and_get_char(prompt,"YyNnVvTt",enter_ok=True)
+	reply = prompt_and_get_char(prompt,'YyNnVvTt',enter_ok=True)
 
 
-	if reply and reply in "YyVvTt":
+	if reply and reply in 'YyVvTt':
 		view_tx_data(c,inputs_data,tx_hex,adata,comment,metadata,
 		view_tx_data(c,inputs_data,tx_hex,adata,comment,metadata,
-				pager=reply in "Vv",terse=reply in "Tt")
+				pager=reply in 'Vv',terse=reply in 'Tt')
 
 
 
 
 def view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pager=False,pause=True,terse=False):
 def view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pager=False,pause=True,terse=False):
 
 
 	td = c.decoderawtransaction(tx_hex)
 	td = c.decoderawtransaction(tx_hex)
 
 
-	fs = "Transaction {} - {} BTC - {} GMT\n" if terse else \
-	"TRANSACTION DATA\n\nHeader: [Tx ID: {}] [Amount: {} BTC] [Time: {}]\n\n"
+	fs = (
+		'TRANSACTION DATA\n\nHeader: [Tx ID: {}] [Amount: {} BTC] [Time: {}]\n\n',
+		'Transaction {} - {} BTC - {} GMT\n'
+	)[bool(terse)]
+
 	out = fs.format(*metadata)
 	out = fs.format(*metadata)
 
 
-	enl = "" if terse else "\n"
-	if comment: out += "Comment: %s\n%s" % (comment,enl)
-	out += "Inputs:\n" + enl
+	enl = ('\n','')[bool(terse)]
+	if comment: out += 'Comment: %s\n%s' % (comment,enl)
+	out += 'Inputs:\n' + enl
 
 
-	nonmm_str = "non-{pnm} address".format(pnm=g.proj_name)
+	nonmm_str = 'non-{pnm} address'.format(pnm=g.proj_name)
 
 
 	total_in = 0
 	total_in = 0
 	for n,i in enumerate(td['vin']):
 	for n,i in enumerate(td['vin']):
@@ -162,93 +164,97 @@ def view_tx_data(c,inputs_data,tx_hex,b2m_map,comment,metadata,pager=False,pause
 				days = int(j['confirmations'] * g.mins_per_block / (60*24))
 				days = int(j['confirmations'] * g.mins_per_block / (60*24))
 				total_in += j['amount']
 				total_in += j['amount']
 				if not j['mmid']: j['mmid'] = nonmm_str
 				if not j['mmid']: j['mmid'] = nonmm_str
-				mmid_fmt = " ({:>{l}})".format(j['mmid'],l=34-len(j['address']))
+				mmid_fmt = ' ({:>{l}})'.format(j['mmid'],l=34-len(j['address']))
 				if terse:
 				if terse:
-					out += "  %s: %-54s %s BTC" % (n+1,j['address'] + mmid_fmt,
+					out += '  %s: %-54s %s BTC' % (n+1,j['address'] + mmid_fmt,
 							trim_exponent(j['amount']))
 							trim_exponent(j['amount']))
 				else:
 				else:
 					for d in (
 					for d in (
-	(n+1, "tx,vout:",       "%s,%s" % (i['txid'], i['vout'])),
-	("",  "address:",       j['address'] + mmid_fmt),
-	("",  "comment:",       j['comment']),
-	("",  "amount:",        "%s BTC" % trim_exponent(j['amount'])),
-	("",  "confirmations:", "%s (around %s days)" % (j['confirmations'], days))
+	(n+1, 'tx,vout:',       '%s,%s' % (i['txid'], i['vout'])),
+	('',  'address:',       j['address'] + mmid_fmt),
+	('',  'comment:',       j['comment']),
+	('',  'amount:',        '%s BTC' % trim_exponent(j['amount'])),
+	('',  'confirmations:', '%s (around %s days)' % (j['confirmations'], days))
 					):
 					):
-						if d[2]: out += ("%3s %-8s %s\n" % d)
-				out += "\n"
+						if d[2]: out += ('%3s %-8s %s\n' % d)
+				out += '\n'
 
 
 				break
 				break
 	total_out = 0
 	total_out = 0
-	out += "Outputs:\n" + enl
+	out += 'Outputs:\n' + enl
 	for n,i in enumerate(td['vout']):
 	for n,i in enumerate(td['vout']):
 		btcaddr = i['scriptPubKey']['addresses'][0]
 		btcaddr = i['scriptPubKey']['addresses'][0]
-		mmid,comment=b2m_map[btcaddr] if btcaddr in b2m_map else (nonmm_str,"")
-		mmid_fmt = " ({:>{l}})".format(mmid,l=34-len(j['address']))
+		mmid,comment=b2m_map[btcaddr] if btcaddr in b2m_map else (nonmm_str,'')
+		mmid_fmt = ' ({:>{l}})'.format(mmid,l=34-len(j['address']))
 		total_out += i['value']
 		total_out += i['value']
 		if terse:
 		if terse:
-			out += "  %s: %-54s %s BTC" % (n+1,btcaddr + mmid_fmt,
+			out += '  %s: %-54s %s BTC' % (n+1,btcaddr + mmid_fmt,
 					trim_exponent(i['value']))
 					trim_exponent(i['value']))
 		else:
 		else:
 			for d in (
 			for d in (
-					(n+1, "address:",  btcaddr + mmid_fmt),
-					("",  "comment:",  comment),
-					("",  "amount:",   trim_exponent(i['value']))
+					(n+1, 'address:',  btcaddr + mmid_fmt),
+					('',  'comment:',  comment),
+					('',  'amount:',   trim_exponent(i['value']))
 				):
 				):
-				if d[2]: out += ("%3s %-8s %s\n" % d)
-		out += "\n"
+				if d[2]: out += ('%3s %-8s %s\n' % d)
+		out += '\n'
+
+	fs = (
+		'Total input:  %s BTC\nTotal output: %s BTC\nTX fee:       %s BTC\n',
+		'In %s BTC - Out %s BTC - Fee %s BTC\n'
+	)[bool(terse)]
 
 
-	fs = "In %s BTC - Out %s BTC - Fee %s BTC\n" if terse else \
-		"Total input:  %s BTC\nTotal output: %s BTC\nTX fee:       %s BTC\n"
 	out += fs % (
 	out += fs % (
 		trim_exponent(total_in),
 		trim_exponent(total_in),
 		trim_exponent(total_out),
 		trim_exponent(total_out),
 		trim_exponent(total_in-total_out)
 		trim_exponent(total_in-total_out)
 	)
 	)
 
 
-	o = out.encode("utf8")
+	o = out.encode('utf8')
 	if pager: do_pager(o)
 	if pager: do_pager(o)
 	else:
 	else:
 		sys.stdout.write(o)
 		sys.stdout.write(o)
+		from mmgen.term import get_char
 		if pause:
 		if pause:
-			get_char("Press any key to continue: ")
-			msg("")
+			get_char('Press any key to continue: ')
+			msg('')
 
 
 
 
 def parse_tx_file(tx_data,infile):
 def parse_tx_file(tx_data,infile):
 
 
-	err_str,err_fmt = "","Invalid %s in transaction file"
+	err_str,err_fmt = '','Invalid %s in transaction file'
 
 
 	if len(tx_data) == 5:
 	if len(tx_data) == 5:
 		metadata,tx_hex,inputs_data,outputs_data,comment = tx_data
 		metadata,tx_hex,inputs_data,outputs_data,comment = tx_data
 	elif len(tx_data) == 4:
 	elif len(tx_data) == 4:
 		metadata,tx_hex,inputs_data,outputs_data = tx_data
 		metadata,tx_hex,inputs_data,outputs_data = tx_data
-		comment = ""
+		comment = ''
 	else:
 	else:
-		err_str = "number of lines"
+		err_str = 'number of lines'
 
 
 	if not err_str:
 	if not err_str:
 		if len(metadata.split()) != 3:
 		if len(metadata.split()) != 3:
-			err_str = "metadata"
+			err_str = 'metadata'
 		else:
 		else:
 			try: unhexlify(tx_hex)
 			try: unhexlify(tx_hex)
-			except: err_str = "hex data"
+			except: err_str = 'hex data'
 			else:
 			else:
 				try: inputs_data = eval(inputs_data)
 				try: inputs_data = eval(inputs_data)
-				except: err_str = "inputs data"
+				except: err_str = 'inputs data'
 				else:
 				else:
 					try: outputs_data = eval(outputs_data)
 					try: outputs_data = eval(outputs_data)
-					except: err_str = "mmgen-to-btc address map data"
+					except: err_str = 'mmgen-to-btc address map data'
 					else:
 					else:
 						if comment:
 						if comment:
 							from mmgen.bitcoin import b58decode
 							from mmgen.bitcoin import b58decode
 							comment = b58decode(comment)
 							comment = b58decode(comment)
 							if comment == False:
 							if comment == False:
-								err_str = "encoded comment (not base58)"
+								err_str = 'encoded comment (not base58)'
 							else:
 							else:
 								if is_valid_tx_comment(comment):
 								if is_valid_tx_comment(comment):
-									comment = comment.decode("utf8")
+									comment = comment.decode('utf8')
 								else:
 								else:
-									err_str = "comment"
+									err_str = 'comment'
 
 
 	if err_str:
 	if err_str:
 		msg(err_fmt % err_str)
 		msg(err_fmt % err_str)
@@ -260,84 +266,75 @@ def parse_tx_file(tx_data,infile):
 def wiftoaddr_keyconv(wif):
 def wiftoaddr_keyconv(wif):
 	if wif[0] == '5':
 	if wif[0] == '5':
 		from subprocess import check_output
 		from subprocess import check_output
-		return check_output(["keyconv", wif]).split()[1]
+		return check_output(['keyconv', wif]).split()[1]
 	else:
 	else:
 		return wiftoaddr(wif)
 		return wiftoaddr(wif)
 
 
 def get_wif2addr_f():
 def get_wif2addr_f():
 	if opt.no_keyconv: return wiftoaddr
 	if opt.no_keyconv: return wiftoaddr
 	from mmgen.addr import test_for_keyconv
 	from mmgen.addr import test_for_keyconv
-	return wiftoaddr_keyconv if test_for_keyconv() else wiftoaddr
+	return (wiftoaddr,wiftoaddr_keyconv)[bool(test_for_keyconv())]
 
 
 
 
 def get_tx_comment_from_file(infile):
 def get_tx_comment_from_file(infile):
-	s = get_data_from_file(infile,"transaction comment")
+	s = get_data_from_file(infile,'transaction comment')
 	if is_valid_tx_comment(s):
 	if is_valid_tx_comment(s):
-		return s.decode("utf8").strip()
+		return s.decode('utf8').strip()
 	else:
 	else:
 		sys.exit(2)
 		sys.exit(2)
 
 
-def get_tx_comment_from_user(comment=""):
+def get_tx_comment_from_user(comment=''):
 	try:
 	try:
 		while True:
 		while True:
-			s = my_raw_input("Comment: ",insert_txt=comment.encode("utf8"))
-			if s == "": return False
+			s = my_raw_input('Comment: ',insert_txt=comment.encode('utf8'))
+			if s == '': return False
 			if is_valid_tx_comment(s):
 			if is_valid_tx_comment(s):
-				return s.decode("utf8")
+				return s.decode('utf8')
 	except KeyboardInterrupt:
 	except KeyboardInterrupt:
-		msg("User interrupt")
+		msg('User interrupt')
 		return False
 		return False
 
 
 def make_tx_data(metadata_fmt, tx_hex, inputs_data, b2m_map, comment):
 def make_tx_data(metadata_fmt, tx_hex, inputs_data, b2m_map, comment):
 	from mmgen.bitcoin import b58encode
 	from mmgen.bitcoin import b58encode
-	s = (b58encode(comment.encode("utf8")),) if comment else ()
+	s = (b58encode(comment.encode('utf8')),) if comment else ()
 	lines = (metadata_fmt, tx_hex, repr(inputs_data), repr(b2m_map)) + s
 	lines = (metadata_fmt, tx_hex, repr(inputs_data), repr(b2m_map)) + s
-	return "\n".join(lines)+"\n"
+	return '\n'.join(lines)+'\n'
 
 
-def mmaddr2btcaddr_addrdata(mmaddr,addr_data,source=""):
-	seed_id,idx = mmaddr.split(":")
+def mmaddr2btcaddr_addrdata(mmaddr,addr_data,source=''):
+	seed_id,idx = mmaddr.split(':')
 	if seed_id in addr_data:
 	if seed_id in addr_data:
 		if idx in addr_data[seed_id]:
 		if idx in addr_data[seed_id]:
-			vmsg("%s -> %s%s" % (mmaddr,addr_data[seed_id][idx][0],
-				" (from "+source+")" if source else ""))
+			vmsg('%s -> %s%s' % (mmaddr,addr_data[seed_id][idx][0],
+				' (from %s)' % source if source else ''))
 			return addr_data[seed_id][idx]
 			return addr_data[seed_id][idx]
 
 
-	return "",""
+	return '',''
 
 
 def get_bitcoind_cfg_options(cfg_keys):
 def get_bitcoind_cfg_options(cfg_keys):
 
 
-	if "HOME" in os.environ:       # Linux
-		homedir,datadir = os.environ["HOME"],".bitcoin"
-	elif "HOMEPATH" in os.environ: # Windows:
-		homedir,data_dir = os.environ["HOMEPATH"],r"Application Data\Bitcoin"
-	else:
-		msg("Neither $HOME nor %HOMEPATH% are set")
-		msg("Don't know where to look for 'bitcoin.conf'")
-		sys.exit(3)
-
-	cfg_file = os.path.join(homedir, datadir, "bitcoin.conf")
+	cfg_file = os.path.join(get_homedir(), get_datadir(), 'bitcoin.conf')
 
 
-	cfg = dict([(k,v) for k,v in [split2(line.translate(None,"\t "),"=")
+	cfg = dict([(k,v) for k,v in [split2(line.translate(None,'\t '),'=')
 			for line in get_lines_from_file(cfg_file)] if k in cfg_keys])
 			for line in get_lines_from_file(cfg_file)] if k in cfg_keys])
 
 
-	for k in set(cfg_keys) - set(cfg.keys()):
-		msg("Configuration option '%s' must be set in %s" % (k,cfg_file))
-		sys.exit(2)
-
+	for k in set(cfg_keys) - set(cfg.keys()): cfg[k] = ''
 	return cfg
 	return cfg
 
 
-def connect_to_bitcoind():
+def get_bitcoind_auth_cookie():
 
 
-	host,port,user,passwd = "localhost",8332,"rpcuser","rpcpassword"
-	cfg = get_bitcoind_cfg_options((user,passwd))
+	f = os.path.join(get_homedir(), get_datadir(), '.cookie')
 
 
-	import mmgen.rpc.connection
-	f = mmgen.rpc.connection.BitcoinConnection
+	if file_is_readable(f):
+		return get_lines_from_file(f)[0]
+	else:
+		return ''
 
 
-	try:
-		c = f(cfg[user],cfg[passwd],host,port)
-	except:
-		msg("Unable to establish RPC connection with bitcoind")
-		sys.exit(2)
+def connect_to_bitcoind():
+
+	host,port,user,passwd = 'localhost',8332,'rpcuser','rpcpassword'
+	cfg = get_bitcoind_cfg_options((user,passwd))
+	auth_cookie = get_bitcoind_auth_cookie()
 
 
-	return c
+	import mmgen.rpc
+	return mmgen.rpc.BitcoinRPCConnection(
+				host,port,cfg[user],cfg[passwd],auth_cookie=auth_cookie)

+ 189 - 178
mmgen/util.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@ import mmgen.globalvars as g
 pnm = g.proj_name
 pnm = g.proj_name
 
 
 _red,_grn,_yel,_cya,_reset,_grnbg = \
 _red,_grn,_yel,_cya,_reset,_grnbg = \
-	["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0","30;102"]
+	['\033[%sm' % c for c in '31;1','32;1','33;1','36;1','0','30;102']
 
 
 def red(s):     return _red+s+_reset
 def red(s):     return _red+s+_reset
 def green(s):   return _grn+s+_reset
 def green(s):   return _grn+s+_reset
@@ -40,10 +40,10 @@ def cyan(s):    return _cya+s+_reset
 def nocolor(s): return s
 def nocolor(s): return s
 
 
 def start_mscolor():
 def start_mscolor():
-	if sys.platform[:3] == "win":
+	if sys.platform[:3] == 'win':
 		global red,green,yellow,cyan,nocolor
 		global red,green,yellow,cyan,nocolor
 		import os
 		import os
-		if "MMGEN_NOMSCOLOR" in os.environ:
+		if 'MMGEN_NOMSCOLOR' in os.environ:
 			red = green = yellow = cyan = grnbg = nocolor
 			red = green = yellow = cyan = grnbg = nocolor
 		else:
 		else:
 			try:
 			try:
@@ -52,47 +52,47 @@ def start_mscolor():
 			except:
 			except:
 				red = green = yellow = cyan = grnbg = nocolor
 				red = green = yellow = cyan = grnbg = nocolor
 
 
-def msg(s):    sys.stderr.write(s+"\n")
+def msg(s):    sys.stderr.write(s+'\n')
 def msg_r(s):  sys.stderr.write(s)
 def msg_r(s):  sys.stderr.write(s)
-def Msg(s):    sys.stdout.write(s + "\n")
+def Msg(s):    sys.stdout.write(s + '\n')
 def Msg_r(s):  sys.stdout.write(s)
 def Msg_r(s):  sys.stdout.write(s)
-def msgred(s): sys.stderr.write(red(s+"\n"))
+def msgred(s): sys.stderr.write(red(s+'\n'))
 def mmsg(*args):
 def mmsg(*args):
 	for d in args:
 	for d in args:
-		sys.stdout.write(repr(d)+"\n")
+		sys.stdout.write(repr(d)+'\n')
 def mdie(*args):
 def mdie(*args):
 	for d in args:
 	for d in args:
-		sys.stdout.write(repr(d)+"\n")
+		sys.stdout.write(repr(d)+'\n')
 	sys.exit()
 	sys.exit()
 
 
 def die(ev,s):
 def die(ev,s):
-	sys.stderr.write(s+"\n"); sys.exit(ev)
+	sys.stderr.write(s+'\n'); sys.exit(ev)
 def Die(ev,s):
 def Die(ev,s):
-	sys.stdout.write(s+"\n"); sys.exit(ev)
+	sys.stdout.write(s+'\n'); sys.exit(ev)
 
 
 def is_mmgen_wallet_label(s):
 def is_mmgen_wallet_label(s):
 	if len(s) > g.max_wallet_label_len:
 	if len(s) > g.max_wallet_label_len:
-		msg("ERROR: wallet label length (%s chars) > maximum allowed (%s chars)" % (len(s),g.max_wallet_label_len))
+		msg('ERROR: wallet label length (%s chars) > maximum allowed (%s chars)' % (len(s),g.max_wallet_label_len))
 		return False
 		return False
 
 
-	try: s = s.decode("utf8")
+	try: s = s.decode('utf8')
 	except: pass
 	except: pass
 
 
 	for ch in s:
 	for ch in s:
 		if ch not in g.wallet_label_symbols:
 		if ch not in g.wallet_label_symbols:
-			msg("ERROR: wallet label contains illegal symbol (%s)" % ch)
+			msg('ERROR: wallet label contains illegal symbol (%s)' % ch)
 			return False
 			return False
 	return True
 	return True
 
 
-# From "man dd":
+# From 'man dd':
 # c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
 # c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
 # GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
 # GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
 
 
 def parse_nbytes(nbytes):
 def parse_nbytes(nbytes):
 	import re
 	import re
 	m = re.match(r'([0123456789]+)(.*)',nbytes)
 	m = re.match(r'([0123456789]+)(.*)',nbytes)
-	smap = ("c",1),("w",2),("b",512),("kB",1000),("K",1024),("MB",1000*1000),\
-			("M",1024*1024),("GB",1000*1000*1000),("G",1024*1024*1024)
+	smap = ('c',1),('w',2),('b',512),('kB',1000),('K',1024),('MB',1000*1000),\
+			('M',1024*1024),('GB',1000*1000*1000),('G',1024*1024*1024)
 	if m:
 	if m:
 		if m.group(2):
 		if m.group(2):
 			for k,v in smap:
 			for k,v in smap:
@@ -103,31 +103,30 @@ def parse_nbytes(nbytes):
 		else:
 		else:
 			return int(nbytes)
 			return int(nbytes)
 
 
-	msg("'%s': invalid byte specifier" % nbytes)
-	sys.exit(1)
+	die(1,"'%s': invalid byte specifier" % nbytes)
 
 
-import opt
+from mmgen.opts import opt
 
 
 def qmsg(s,alt=False):
 def qmsg(s,alt=False):
 	if opt.quiet:
 	if opt.quiet:
-		if alt != False: sys.stderr.write(alt + "\n")
-	else: sys.stderr.write(s + "\n")
+		if alt != False: sys.stderr.write(alt + '\n')
+	else: sys.stderr.write(s + '\n')
 def qmsg_r(s,alt=False):
 def qmsg_r(s,alt=False):
 	if opt.quiet:
 	if opt.quiet:
 		if alt != False: sys.stderr.write(alt)
 		if alt != False: sys.stderr.write(alt)
 	else: sys.stderr.write(s)
 	else: sys.stderr.write(s)
 def vmsg(s):
 def vmsg(s):
-	if opt.verbose: sys.stderr.write(s + "\n")
+	if opt.verbose: sys.stderr.write(s + '\n')
 def vmsg_r(s):
 def vmsg_r(s):
 	if opt.verbose: sys.stderr.write(s)
 	if opt.verbose: sys.stderr.write(s)
 
 
 def Vmsg(s):
 def Vmsg(s):
-	if opt.verbose: sys.stdout.write(s + "\n")
+	if opt.verbose: sys.stdout.write(s + '\n')
 def Vmsg_r(s):
 def Vmsg_r(s):
 	if opt.verbose: sys.stdout.write(s)
 	if opt.verbose: sys.stdout.write(s)
 
 
 def dmsg(s):
 def dmsg(s):
-	if opt.debug: sys.stdout.write(s + "\n")
+	if opt.debug: sys.stdout.write(s + '\n')
 
 
 def suf(arg,suf_type):
 def suf(arg,suf_type):
 	t = type(arg)
 	t = type(arg)
@@ -136,31 +135,29 @@ def suf(arg,suf_type):
 	elif t == list or t == tuple or t == set:
 	elif t == list or t == tuple or t == set:
 		n = len(arg)
 		n = len(arg)
 	else:
 	else:
-		msg("%s: invalid parameter" % arg)
-		return ""
+		msg('%s: invalid parameter' % arg)
+		return ''
 
 
-	if suf_type in ("a","es"):
-		return "" if n == 1 else "es"
-	if suf_type in ("k","s"):
-		return "" if n == 1 else "s"
+	if suf_type in ('a','es'): return ('es','')[n == 1]
+	if suf_type in ('k','s'):  return ('s','')[n == 1]
 
 
 def get_extension(f):
 def get_extension(f):
 	a,b = os.path.splitext(f)
 	a,b = os.path.splitext(f)
-	return ('',b[1:])[int(len(b) > 1)]
+	return ('',b[1:])[len(b) > 1]
 
 
 def remove_extension(f,e):
 def remove_extension(f,e):
 	a,b = os.path.splitext(f)
 	a,b = os.path.splitext(f)
-	return (f,a)[int(len(b) > 1 and b[1:] == e)]
+	return (f,a)[len(b)>1 and b[1:]==e]
 
 
 def make_chksum_N(s,nchars,sep=False):
 def make_chksum_N(s,nchars,sep=False):
 	if nchars%4 or not (4 <= nchars <= 64): return False
 	if nchars%4 or not (4 <= nchars <= 64): return False
 	s = sha256(sha256(s).digest()).hexdigest().upper()
 	s = sha256(sha256(s).digest()).hexdigest().upper()
-	sep = " " if sep else ""
+	sep = ('',' ')[bool(sep)]
 	return sep.join([s[i*4:i*4+4] for i in range(nchars/4)])
 	return sep.join([s[i*4:i*4+4] for i in range(nchars/4)])
 
 
 def make_chksum_8(s,sep=False):
 def make_chksum_8(s,sep=False):
 	s = sha256(sha256(s).digest()).hexdigest()[:8].upper()
 	s = sha256(sha256(s).digest()).hexdigest()[:8].upper()
-	return "{} {}".format(s[:4],s[4:]) if sep else s
+	return '{} {}'.format(s[:4],s[4:]) if sep else s
 def make_chksum_6(s): return sha256(s).hexdigest()[:6]
 def make_chksum_6(s): return sha256(s).hexdigest()[:6]
 def is_chksum_6(s): return len(s) == 6 and is_hexstring_lc(s)
 def is_chksum_6(s): return len(s) == 6 and is_hexstring_lc(s)
 
 
@@ -168,26 +165,25 @@ def make_iv_chksum(s): return sha256(s).hexdigest()[:8].upper()
 
 
 def splitN(s,n,sep=None):                      # always return an n-element list
 def splitN(s,n,sep=None):                      # always return an n-element list
 	ret = s.split(sep,n-1)
 	ret = s.split(sep,n-1)
-	return ret + ["" for i in range(n-len(ret))]
+	return ret + ['' for i in range(n-len(ret))]
 def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
 def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
 def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
 def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
 
 
 def split_into_cols(col_wid,s):
 def split_into_cols(col_wid,s):
-	return " ".join([s[col_wid*i:col_wid*(i+1)]
+	return ' '.join([s[col_wid*i:col_wid*(i+1)]
 					for i in range(len(s)/col_wid+1)]).rstrip()
 					for i in range(len(s)/col_wid+1)]).rstrip()
 
 
 def capfirst(s):
 def capfirst(s):
-	return s if len(s) == 0 else \
-		(s[0].upper() + (s[1:] if len(s) > 1 else ""))
+	return s if len(s) == 0 else s[0].upper() + s[1:]
 
 
 def make_timestamp():
 def make_timestamp():
 	tv = time.gmtime(time.time())[:6]
 	tv = time.gmtime(time.time())[:6]
-	return "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
+	return '{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}'.format(*tv)
 def make_timestr():
 def make_timestr():
 	tv = time.gmtime(time.time())[:6]
 	tv = time.gmtime(time.time())[:6]
-	return "{:04d}/{:02d}/{:02d} {:02d}:{:02d}:{:02d}".format(*tv)
+	return '{:04d}/{:02d}/{:02d} {:02d}:{:02d}:{:02d}'.format(*tv)
 def secs_to_hms(secs):
 def secs_to_hms(secs):
-	return "{:02d}:{:02d}:{:02d}".format(secs/3600, (secs/60) % 60, secs % 60)
+	return '{:02d}:{:02d}:{:02d}'.format(secs/3600, (secs/60) % 60, secs % 60)
 
 
 def _is_whatstring(s,chars):
 def _is_whatstring(s,chars):
 	return set(list(s)) <= set(chars)
 	return set(list(s)) <= set(chars)
@@ -210,17 +206,17 @@ def is_b58string(s):
 	return _is_whatstring(s,b58a)
 	return _is_whatstring(s,b58a)
 
 
 def is_utf8(s):
 def is_utf8(s):
-	try: s.decode("utf8")
+	try: s.decode('utf8')
 	except: return False
 	except: return False
 	else: return True
 	else: return True
 
 
 def is_ascii(s):
 def is_ascii(s):
-	try: s.decode("ascii")
+	try: s.decode('ascii')
 	except: return False
 	except: return False
 	else: return True
 	else: return True
 
 
 def match_ext(addr,ext):
 def match_ext(addr,ext):
-	return addr.split(".")[-1] == ext
+	return addr.split('.')[-1] == ext
 
 
 def file_exists(f):
 def file_exists(f):
 	try:
 	try:
@@ -229,106 +225,120 @@ def file_exists(f):
 	except:
 	except:
 		return False
 		return False
 
 
+def file_is_readable(f):
+	from stat import S_IREAD
+	try:
+		assert os.stat(f).st_mode & S_IREAD
+	except:
+		return False
+	else:
+		return True
+
+def get_homedir():
+	if 'HOME' in os.environ:       # Linux
+		return os.environ['HOME']
+	elif 'HOMEPATH' in os.environ: # Windows:
+		return os.environ['HOMEPATH']
+	else:
+		msg('Neither $HOME nor %HOMEPATH% are set')
+		die(2,"Don't know where to look for bitcoin data directory")
+
+def get_datadir():
+	return (r'Application Data\Bitcoin','.bitcoin')['HOME' in os.environ]
+
 def get_from_brain_opt_params():
 def get_from_brain_opt_params():
-	l,p = opt.from_brain.split(",")
+	l,p = opt.from_brain.split(',')
 	return(int(l),p)
 	return(int(l),p)
 
 
 def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
 def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
-	r = 1 if len(data) % gw else 0
-	return "".join(
+	r = (0,1)[bool(len(data) % gw)]
+	return ''.join(
 		[
 		[
-			("" if (line_nums == False or i % cols) else "{:06x}: ".format(i*gw)) +
-			hexlify(data[i*gw:i*gw+gw]) +
-			(" " if (i+1) % cols else "\n")
-				for i in range(len(data)/gw + r)
+			('' if (line_nums == False or i % cols) else '{:06x}: '.format(i*gw)) +
+				hexlify(data[i*gw:i*gw+gw]) + ('\n',' ')[bool((i+1) % cols)]
+					for i in range(len(data)/gw + r)
 		]
 		]
-	).rstrip() + "\n"
+	).rstrip() + '\n'
 
 
 def decode_pretty_hexdump(data):
 def decode_pretty_hexdump(data):
 	from string import hexdigits
 	from string import hexdigits
 	pat = r'^[%s]+:\s+' % hexdigits
 	pat = r'^[%s]+:\s+' % hexdigits
 	lines = [re.sub(pat,'',l) for l in data.splitlines()]
 	lines = [re.sub(pat,'',l) for l in data.splitlines()]
 	try:
 	try:
-		return unhexlify("".join(("".join(lines).split())))
+		return unhexlify(''.join((''.join(lines).split())))
 	except:
 	except:
-		msg("Data not in hexdump format")
+		msg('Data not in hexdump format')
 		return False
 		return False
 
 
 def get_hash_params(hash_preset):
 def get_hash_params(hash_preset):
 	if hash_preset in g.hash_presets:
 	if hash_preset in g.hash_presets:
 		return g.hash_presets[hash_preset] # N,p,r,buflen
 		return g.hash_presets[hash_preset] # N,p,r,buflen
 	else: # Shouldn't be here
 	else: # Shouldn't be here
-		msg("%s: invalid 'hash_preset' value" % hash_preset)
-		sys.exit(3)
+		die(3,"%s: invalid 'hash_preset' value" % hash_preset)
 
 
-def compare_chksums(chk1, desc1, chk2, desc2, hdr="", die_on_fail=False):
+def compare_chksums(chk1, desc1, chk2, desc2, hdr='', die_on_fail=False):
 
 
 	if not chk1 == chk2:
 	if not chk1 == chk2:
 		m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
 		m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
-				% ((hdr+":\n   " if hdr else "CHECKSUM"),desc2,chk2,desc1,chk1)
+				% ((hdr+':\n   ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
 		if die_on_fail:
 		if die_on_fail:
 			die(3,m)
 			die(3,m)
 		else:
 		else:
 			vmsg(m)
 			vmsg(m)
 			return False
 			return False
 
 
-	vmsg("%s checksum OK (%s)" % (capfirst(desc1),chk1))
+	vmsg('%s checksum OK (%s)' % (capfirst(desc1),chk1))
 	return True
 	return True
 
 
-def compare_or_die(val1, desc1, val2, desc2, e="Error"):
+def compare_or_die(val1, desc1, val2, desc2, e='Error'):
 	if cmp(val1,val2):
 	if cmp(val1,val2):
 		die(3,"%s: %s (%s) doesn't match %s (%s)"
 		die(3,"%s: %s (%s) doesn't match %s (%s)"
 				% (e,desc2,val2,desc1,val1))
 				% (e,desc2,val2,desc1,val1))
-	dmsg("%s OK (%s)" % (capfirst(desc2),val2))
+	dmsg('%s OK (%s)' % (capfirst(desc2),val2))
 	return True
 	return True
 
 
 def open_file_or_exit(filename,mode):
 def open_file_or_exit(filename,mode):
 	try:
 	try:
 		f = open(filename, mode)
 		f = open(filename, mode)
 	except:
 	except:
-		op = ("writing","reading")[int('r' in mode)]
+		op = ('writing','reading')['r' in mode]
 		die(2,"Unable to open file '%s' for %s" % (filename,op))
 		die(2,"Unable to open file '%s' for %s" % (filename,op))
 	return f
 	return f
 
 
 
 
 def check_file_type_and_access(fname,ftype,blkdev_ok=False):
 def check_file_type_and_access(fname,ftype,blkdev_ok=False):
 
 
-	import os, stat
-
-	a = ((os.R_OK,"read"),(os.W_OK,"writ"))
-	access,m = a[int(ftype in ("output file","output directory"))]
+	a = ((os.R_OK,'read'),(os.W_OK,'writ'))
+	access,m = a[ftype in ('output file','output directory')]
 
 
 	ok_types = [
 	ok_types = [
-		(stat.S_ISREG,"regular file"),
-		(stat.S_ISLNK,"symbolic link")
+		(stat.S_ISREG,'regular file'),
+		(stat.S_ISLNK,'symbolic link')
 	]
 	]
-	if blkdev_ok: ok_types.append((stat.S_ISBLK,"block device"))
-	if ftype == "output directory": ok_types = [(stat.S_ISDIR, "output directory")]
+	if blkdev_ok: ok_types.append((stat.S_ISBLK,'block device'))
+	if ftype == 'output directory': ok_types = [(stat.S_ISDIR, 'output directory')]
 
 
 	try: mode = os.stat(fname).st_mode
 	try: mode = os.stat(fname).st_mode
 	except:
 	except:
-		msg("Unable to stat requested %s '%s'" % (ftype,fname))
-		sys.exit(1)
+		die(1,"Unable to stat requested %s '%s'" % (ftype,fname))
 
 
 	for t in ok_types:
 	for t in ok_types:
 		if t[0](mode): break
 		if t[0](mode): break
 	else:
 	else:
-		msg("Requested %s '%s' is not a %s" % (ftype,fname,
-				" or ".join([t[1] for t in ok_types])))
-		sys.exit(1)
+		die(1,"Requested %s '%s' is not a %s" % (ftype,fname,
+				' or '.join([t[1] for t in ok_types])))
 
 
 	if not os.access(fname, access):
 	if not os.access(fname, access):
-		msg("Requested %s '%s' is not %sable by you" % (ftype,fname,m))
-		sys.exit(1)
+		die(1,"Requested %s '%s' is not %sable by you" % (ftype,fname,m))
 
 
 	return True
 	return True
 
 
 def check_infile(f,blkdev_ok=False):
 def check_infile(f,blkdev_ok=False):
-	return check_file_type_and_access(f,"input file",blkdev_ok=blkdev_ok)
+	return check_file_type_and_access(f,'input file',blkdev_ok=blkdev_ok)
 def check_outfile(f,blkdev_ok=False):
 def check_outfile(f,blkdev_ok=False):
-	return check_file_type_and_access(f,"output file",blkdev_ok=blkdev_ok)
+	return check_file_type_and_access(f,'output file',blkdev_ok=blkdev_ok)
 def check_outdir(f):
 def check_outdir(f):
-	return check_file_type_and_access(f,"output directory")
+	return check_file_type_and_access(f,'output directory')
 
 
 def make_full_path(outdir,outfile):
 def make_full_path(outdir,outfile):
 	return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
 	return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
@@ -341,13 +351,13 @@ def _validate_addr_num(n):
 		msg("'%s': invalid %s address index" % (n,g.proj_name))
 		msg("'%s': invalid %s address index" % (n,g.proj_name))
 		return False
 		return False
 
 
-def parse_addr_idxs(arg,sep=","):
+def parse_addr_idxs(arg,sep=','):
 
 
 	ret = []
 	ret = []
 
 
 	for i in (arg.split(sep)):
 	for i in (arg.split(sep)):
 
 
-		j = i.split("-")
+		j = i.split('-')
 
 
 		if len(j) == 1:
 		if len(j) == 1:
 			i = _validate_addr_num(i)
 			i = _validate_addr_num(i)
@@ -371,48 +381,47 @@ def parse_addr_idxs(arg,sep=","):
 
 
 def get_new_passphrase(desc,passchg=False):
 def get_new_passphrase(desc,passchg=False):
 
 
-	w = "{}passphrase for {}".format("new " if passchg else "", desc)
+	w = '{}passphrase for {}'.format(('','new ')[bool(passchg)], desc)
 	if opt.passwd_file:
 	if opt.passwd_file:
-		pw = " ".join(get_words_from_file(opt.passwd_file,w))
+		pw = ' '.join(get_words_from_file(opt.passwd_file,w))
 	elif opt.echo_passphrase:
 	elif opt.echo_passphrase:
-		pw = " ".join(get_words_from_user("Enter {}: ".format(w)))
+		pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
 	else:
 	else:
 		for i in range(g.passwd_max_tries):
 		for i in range(g.passwd_max_tries):
-			pw = " ".join(get_words_from_user("Enter {}: ".format(w)))
-			pw2 = " ".join(get_words_from_user("Repeat passphrase: "))
-			dmsg("Passphrases: [%s] [%s]" % (pw,pw2))
+			pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
+			pw2 = ' '.join(get_words_from_user('Repeat passphrase: '))
+			dmsg('Passphrases: [%s] [%s]' % (pw,pw2))
 			if pw == pw2:
 			if pw == pw2:
-				vmsg("Passphrases match"); break
-			else: msg("Passphrases do not match.  Try again.")
+				vmsg('Passphrases match'); break
+			else: msg('Passphrases do not match.  Try again.')
 		else:
 		else:
-			msg("User failed to duplicate passphrase in %s attempts" %
+			die(2,'User failed to duplicate passphrase in %s attempts' %
 					g.passwd_max_tries)
 					g.passwd_max_tries)
-			sys.exit(2)
 
 
-	if pw == "": qmsg("WARNING: Empty passphrase")
+	if pw == '': qmsg('WARNING: Empty passphrase')
 	return pw
 	return pw
 
 
 
 
-def confirm_or_exit(message, question, expect="YES"):
+def confirm_or_exit(message, question, expect='YES'):
 
 
 	m = message.strip()
 	m = message.strip()
 	if m: msg(m)
 	if m: msg(m)
 
 
-	a = question+"  " if question[0].isupper() else \
-			"Are you sure you want to %s?\n" % question
+	a = question+'  ' if question[0].isupper() else \
+			'Are you sure you want to %s?\n' % question
 	b = "Type uppercase '%s' to confirm: " % expect
 	b = "Type uppercase '%s' to confirm: " % expect
 
 
 	if my_raw_input(a+b).strip() != expect:
 	if my_raw_input(a+b).strip() != expect:
-		die(2,"Exiting at user request")
+		die(2,'Exiting at user request')
 
 
 
 
 # New function
 # New function
 def write_data_to_file(
 def write_data_to_file(
 		outfile,
 		outfile,
 		data,
 		data,
-		desc="data",
+		desc='data',
 		ask_write=False,
 		ask_write=False,
-		ask_write_prompt="",
+		ask_write_prompt='',
 		ask_write_default_yes=True,
 		ask_write_default_yes=True,
 		ask_overwrite=True,
 		ask_overwrite=True,
 		ask_tty=True,
 		ask_tty=True,
@@ -428,30 +437,29 @@ def write_data_to_file(
 		ask_write = True
 		ask_write = True
 
 
 	if opt.stdout or not sys.stdout.isatty() or outfile in ('','-'):
 	if opt.stdout or not sys.stdout.isatty() or outfile in ('','-'):
-		qmsg("Output to STDOUT requested")
+		qmsg('Output to STDOUT requested')
 		if sys.stdout.isatty():
 		if sys.stdout.isatty():
 			if no_tty:
 			if no_tty:
-				die(2,"Printing %s to screen is not allowed" % desc)
+				die(2,'Printing %s to screen is not allowed' % desc)
 			if ask_tty and not opt.quiet:
 			if ask_tty and not opt.quiet:
-				confirm_or_exit("",'output %s to screen' % desc)
+				confirm_or_exit('','output %s to screen' % desc)
 		else:
 		else:
-			try:    of = os.readlink("/proc/%d/fd/1" % os.getpid()) # Linux
+			try:    of = os.readlink('/proc/%d/fd/1' % os.getpid()) # Linux
 			except: of = None # Windows
 			except: of = None # Windows
 
 
 			if of:
 			if of:
-				if of[:5] == "pipe:":
+				if of[:5] == 'pipe:':
 					if no_tty:
 					if no_tty:
-						die(2,"Writing %s to pipe is not allowed" % desc)
+						die(2,'Writing %s to pipe is not allowed' % desc)
 					if ask_tty and not opt.quiet:
 					if ask_tty and not opt.quiet:
-						confirm_or_exit("",'output %s to pipe' % desc)
-						msg("")
+						confirm_or_exit('','output %s to pipe' % desc)
+						msg('')
 				of2,pd = os.path.relpath(of),os.path.pardir
 				of2,pd = os.path.relpath(of),os.path.pardir
-				msg("Redirecting output to file '%s'" %
-						(of if of2[:len(pd)] == pd else of2))
+				msg("Redirecting output to file '%s'" % (of2,of)[of2[:len(pd)] == pd])
 			else:
 			else:
-				msg("Redirecting output to file")
+				msg('Redirecting output to file')
 
 
-		if binary and sys.platform[:3] == "win":
+		if binary and sys.platform[:3] == 'win':
 			import msvcrt
 			import msvcrt
 			msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
 			msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
 
 
@@ -460,19 +468,19 @@ def write_data_to_file(
 		if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 		if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 
 		if ask_write:
 		if ask_write:
-			if not ask_write_prompt: ask_write_prompt = "Save %s?" % desc
+			if not ask_write_prompt: ask_write_prompt = 'Save %s?' % desc
 			if not keypress_confirm(ask_write_prompt,
 			if not keypress_confirm(ask_write_prompt,
 						default_yes=ask_write_default_yes):
 						default_yes=ask_write_default_yes):
-				die(1,"%s not saved" % capfirst(desc))
+				die(1,'%s not saved' % capfirst(desc))
 
 
 		hush = False
 		hush = False
 		if file_exists(outfile) and ask_overwrite:
 		if file_exists(outfile) and ask_overwrite:
 			q = "File '%s' already exists\nOverwrite?" % outfile
 			q = "File '%s' already exists\nOverwrite?" % outfile
-			confirm_or_exit("",q)
+			confirm_or_exit('',q)
 			msg("Overwriting file '%s'" % outfile)
 			msg("Overwriting file '%s'" % outfile)
 			hush = True
 			hush = True
 
 
-		f = open_file_or_exit(outfile,'w'+('','b')[int(binary)])
+		f = open_file_or_exit(outfile,('w','wb')[bool(binary)])
 		try:
 		try:
 			f.write(data)
 			f.write(data)
 		except:
 		except:
@@ -484,23 +492,22 @@ def write_data_to_file(
 
 
 		return True
 		return True
 
 
-from mmgen.bitcoin import b58decode_pad,b58encode_pad
 
 
 def _check_mmseed_format(words):
 def _check_mmseed_format(words):
 
 
 	valid = False
 	valid = False
-	desc = "%s data" % g.seed_ext
+	desc = '%s data' % g.seed_ext
 	try:
 	try:
 		chklen = len(words[0])
 		chklen = len(words[0])
 	except:
 	except:
 		return False
 		return False
 
 
 	if len(words) < 3 or len(words) > 12:
 	if len(words) < 3 or len(words) > 12:
-		msg("Invalid data length (%s) in %s" % (len(words),desc))
+		msg('Invalid data length (%s) in %s' % (len(words),desc))
 	elif not is_hexstring(words[0]):
 	elif not is_hexstring(words[0]):
 		msg("Invalid format of checksum '%s' in %s"%(words[0], desc))
 		msg("Invalid format of checksum '%s' in %s"%(words[0], desc))
 	elif chklen != 6:
 	elif chklen != 6:
-		msg("Incorrect length of checksum (%s) in %s" % (chklen,desc))
+		msg('Incorrect length of checksum (%s) in %s' % (chklen,desc))
 	else: valid = True
 	else: valid = True
 
 
 	return valid
 	return valid
@@ -512,31 +519,29 @@ def _check_wallet_format(infile, lines):
 	valid = False
 	valid = False
 	chklen = len(lines[0])
 	chklen = len(lines[0])
 	if len(lines) != 6:
 	if len(lines) != 6:
-		vmsg("Invalid number of lines (%s) in %s" % (len(lines),desc))
+		vmsg('Invalid number of lines (%s) in %s' % (len(lines),desc))
 	elif chklen != 6:
 	elif chklen != 6:
-		vmsg("Incorrect length of Master checksum (%s) in %s" % (chklen,desc))
+		vmsg('Incorrect length of Master checksum (%s) in %s' % (chklen,desc))
 	elif not is_hexstring(lines[0]):
 	elif not is_hexstring(lines[0]):
 		vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], desc))
 		vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], desc))
 	else: valid = True
 	else: valid = True
 
 
 	if valid == False:
 	if valid == False:
-		msg("Invalid %s" % desc)
-		sys.exit(2)
+		die(2,'Invalid %s' % desc)
 
 
 
 
 def _check_chksum_6(chk,val,desc,infile):
 def _check_chksum_6(chk,val,desc,infile):
 	comp_chk = make_chksum_6(val)
 	comp_chk = make_chksum_6(val)
 	if chk != comp_chk:
 	if chk != comp_chk:
 		msg("%s checksum incorrect in file '%s'!" % (desc,infile))
 		msg("%s checksum incorrect in file '%s'!" % (desc,infile))
-		msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
-		sys.exit(2)
-	dmsg("%s checksum passed: %s" % (capfirst(desc),chk))
+		die(2,'Checksum: %s. Computed value: %s' % (chk,comp_chk))
+	dmsg('%s checksum passed: %s' % (capfirst(desc),chk))
 
 
 
 
 def get_words_from_user(prompt):
 def get_words_from_user(prompt):
 	# split() also strips
 	# split() also strips
 	words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
 	words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
-	dmsg("Sanitized input: [%s]" % " ".join(words))
+	dmsg('Sanitized input: [%s]' % ' '.join(words))
 	return words
 	return words
 
 
 
 
@@ -547,7 +552,7 @@ def get_words_from_file(infile,desc,silent=False):
 	# split() also strips
 	# split() also strips
 	words = f.read().split()
 	words = f.read().split()
 	f.close()
 	f.close()
-	dmsg("Sanitized input: [%s]" % " ".join(words))
+	dmsg('Sanitized input: [%s]' % ' '.join(words))
 	return words
 	return words
 
 
 
 
@@ -566,8 +571,8 @@ def remove_comments(lines):
 		if i: ret.append(i)
 		if i: ret.append(i)
 	return ret
 	return ret
 
 
-def get_lines_from_file(infile,desc="",trim_comments=False):
-	if desc != "":
+def get_lines_from_file(infile,desc='',trim_comments=False):
+	if desc != '':
 		qmsg("Getting %s from file '%s'" % (desc,infile))
 		qmsg("Getting %s from file '%s'" % (desc,infile))
 	f = open_file_or_exit(infile,'r')
 	f = open_file_or_exit(infile,'r')
 	lines = f.read().splitlines() # DOS-safe
 	lines = f.read().splitlines() # DOS-safe
@@ -575,16 +580,16 @@ def get_lines_from_file(infile,desc="",trim_comments=False):
 	return remove_comments(lines) if trim_comments else lines
 	return remove_comments(lines) if trim_comments else lines
 
 
 
 
-def get_data_from_user(desc="data",silent=False):
-	data = my_raw_input("Enter %s: " % desc, echo=opt.echo_passphrase)
-	dmsg("User input: [%s]" % data)
+def get_data_from_user(desc='data',silent=False):
+	data = my_raw_input('Enter %s: ' % desc, echo=opt.echo_passphrase)
+	dmsg('User input: [%s]' % data)
 	return data
 	return data
 
 
-def get_data_from_file(infile,desc="data",dash=False,silent=False,binary=False):
-	if dash and infile == "-": return sys.stdin.read()
+def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False):
+	if dash and infile == '-': return sys.stdin.read()
 	if not silent:
 	if not silent:
 		qmsg("Getting %s from file '%s'" % (desc,infile))
 		qmsg("Getting %s from file '%s'" % (desc,infile))
-	f = open_file_or_exit(infile,'r'+('','b')[int(binary)])
+	f = open_file_or_exit(infile,('r','rb')[bool(binary)])
 	data = f.read()
 	data = f.read()
 	f.close()
 	f.close()
 	return data
 	return data
@@ -593,25 +598,26 @@ def get_data_from_file(infile,desc="data",dash=False,silent=False,binary=False):
 def get_seed_from_seed_data(words):
 def get_seed_from_seed_data(words):
 
 
 	if not _check_mmseed_format(words):
 	if not _check_mmseed_format(words):
-		msg("Invalid %s data" % g.seed_ext)
+		msg('Invalid %s data' % g.seed_ext)
 		return False
 		return False
 
 
 	stored_chk = words[0]
 	stored_chk = words[0]
-	seed_b58 = "".join(words[1:])
+	seed_b58 = ''.join(words[1:])
 
 
 	chk = make_chksum_6(seed_b58)
 	chk = make_chksum_6(seed_b58)
-	vmsg_r("Validating %s checksum..." % g.seed_ext)
+	vmsg_r('Validating %s checksum...' % g.seed_ext)
 
 
-	if compare_chksums(chk, "seed", stored_chk, "input"):
+	if compare_chksums(chk, 'seed', stored_chk, 'input'):
+		from mmgen.bitcoin import b58decode_pad
 		seed = b58decode_pad(seed_b58)
 		seed = b58decode_pad(seed_b58)
 		if seed == False:
 		if seed == False:
-			msg("Invalid b58 number: %s" % val)
+			msg('Invalid b58 number: %s' % val)
 			return False
 			return False
 
 
-		msg("Valid seed data for Seed ID %s" % make_chksum_8(seed))
+		msg('Valid seed data for Seed ID %s' % make_chksum_8(seed))
 		return seed
 		return seed
 	else:
 	else:
-		msg("Invalid checksum for {pnm} seed".format(pnm=pnm))
+		msg('Invalid checksum for {pnm} seed'.format(pnm=pnm))
 		return False
 		return False
 
 
 
 
@@ -627,18 +633,18 @@ def pwfile_reuse_warning():
 
 
 
 
 def get_mmgen_passphrase(desc,passchg=False):
 def get_mmgen_passphrase(desc,passchg=False):
-	prompt ="Enter {}passphrase for {}: ".format("old " if passchg else "",desc)
+	prompt ='Enter {}passphrase for {}: '.format(('','old ')[bool(passchg)],desc)
 	if opt.passwd_file:
 	if opt.passwd_file:
 		pwfile_reuse_warning()
 		pwfile_reuse_warning()
-		return " ".join(get_words_from_file(opt.passwd_file,"passphrase"))
+		return ' '.join(get_words_from_file(opt.passwd_file,'passphrase'))
 	else:
 	else:
-		return " ".join(get_words_from_user(prompt))
+		return ' '.join(get_words_from_user(prompt))
 
 
 
 
 def get_bitcoind_passphrase(prompt):
 def get_bitcoind_passphrase(prompt):
 	if opt.passwd_file:
 	if opt.passwd_file:
 		pwfile_reuse_warning()
 		pwfile_reuse_warning()
-		return get_data_from_file(opt.passwd_file,"passphrase").strip("\r\n")
+		return get_data_from_file(opt.passwd_file,'passphrase').strip('\r\n')
 	else:
 	else:
 		return my_raw_input(prompt, echo=opt.echo_passphrase)
 		return my_raw_input(prompt, echo=opt.echo_passphrase)
 
 
@@ -653,16 +659,13 @@ def check_data_fits_file_at_offset(fname,offset,dlen,action):
 		fsize = os.stat(fname).st_size
 		fsize = os.stat(fname).st_size
 
 
 	if fsize < offset + dlen:
 	if fsize < offset + dlen:
-		m = "Destination" if action == "write" else "Input"
-		msg(
-	"%s file has length %s, too short to %s %s bytes of data at offset %s"
+		m = ('Input','Destination')[action == 'write']
+		die(1,
+	'%s file has length %s, too short to %s %s bytes of data at offset %s'
 			% (m,fsize,action,dlen,offset))
 			% (m,fsize,action,dlen,offset))
-		sys.exit(1)
-
 
 
-from mmgen.term import kb_hold_protect,get_char
 
 
-def get_hash_preset_from_user(hp=g.hash_preset,desc="data"):
+def get_hash_preset_from_user(hp=g.hash_preset,desc='data'):
 	p = """Enter hash preset for %s,
 	p = """Enter hash preset for %s,
  or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
  or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
 	while True:
 	while True:
@@ -670,13 +673,13 @@ def get_hash_preset_from_user(hp=g.hash_preset,desc="data"):
 		if ret:
 		if ret:
 			if ret in g.hash_presets.keys(): return ret
 			if ret in g.hash_presets.keys(): return ret
 			else:
 			else:
-				msg("Invalid input.  Valid choices are %s" %
-						", ".join(sorted(g.hash_presets.keys())))
+				msg('Invalid input.  Valid choices are %s' %
+						', '.join(sorted(g.hash_presets.keys())))
 				continue
 				continue
 		else: return hp
 		else: return hp
 
 
 
 
-def my_raw_input(prompt,echo=True,insert_txt="",use_readline=True):
+def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
 
 
 	try: import readline
 	try: import readline
 	except: use_readline = False # Windows
 	except: use_readline = False # Windows
@@ -686,7 +689,9 @@ def my_raw_input(prompt,echo=True,insert_txt="",use_readline=True):
 		readline.set_startup_hook(st_hook)
 		readline.set_startup_hook(st_hook)
 	else:
 	else:
 		msg_r(prompt)
 		msg_r(prompt)
-		prompt = ""
+		prompt = ''
+
+	from mmgen.term import kb_hold_protect
 
 
 	kb_hold_protect()
 	kb_hold_protect()
 	if echo:
 	if echo:
@@ -701,50 +706,56 @@ def my_raw_input(prompt,echo=True,insert_txt="",use_readline=True):
 
 
 def keypress_confirm(prompt,default_yes=False,verbose=False):
 def keypress_confirm(prompt,default_yes=False,verbose=False):
 
 
-	q = "(Y/n)" if default_yes else "(y/N)"
+	from mmgen.term import get_char
+
+	q = ('(y/N)','(Y/n)')[bool(default_yes)]
 
 
 	while True:
 	while True:
-		reply = get_char("%s %s: " % (prompt, q)).strip("\n\r")
+		reply = get_char('%s %s: ' % (prompt, q)).strip('\n\r')
 
 
 		if not reply:
 		if not reply:
-			if default_yes: msg(""); return True
-			else:           msg(""); return False
-		elif reply in 'yY': msg(""); return True
-		elif reply in 'nN': msg(""); return False
+			if default_yes: msg(''); return True
+			else:           msg(''); return False
+		elif reply in 'yY': msg(''); return True
+		elif reply in 'nN': msg(''); return False
 		else:
 		else:
-			if verbose: msg("\nInvalid reply")
-			else: msg_r("\r")
+			if verbose: msg('\nInvalid reply')
+			else: msg_r('\r')
 
 
 
 
 def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
 def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
 
 
+	from mmgen.term import get_char
+
 	while True:
 	while True:
-		reply = get_char("%s: " % prompt).strip("\n\r")
+		reply = get_char('%s: ' % prompt).strip('\n\r')
 
 
 		if reply in chars or (enter_ok and not reply):
 		if reply in chars or (enter_ok and not reply):
-			msg("")
+			msg('')
 			return reply
 			return reply
 
 
-		if verbose: msg("\nInvalid reply")
-		else: msg_r("\r")
+		if verbose: msg('\nInvalid reply')
+		else: msg_r('\r')
 
 
 
 
 def do_license_msg(immed=False):
 def do_license_msg(immed=False):
 
 
-	import mmgen.license as gpl
 	if opt.quiet or g.no_license: return
 	if opt.quiet or g.no_license: return
 
 
+	import mmgen.license as gpl
+
 	p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
 	p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
 	msg(gpl.warning)
 	msg(gpl.warning)
-	prompt = "%s " % p.strip()
+	prompt = '%s ' % p.strip()
+
+	from mmgen.term import get_char,do_pager
 
 
 	while True:
 	while True:
-		reply = get_char(prompt, immed_chars="wc" if immed else "")
+		reply = get_char(prompt, immed_chars=('','wc')[bool(immed)])
 		if reply == 'w':
 		if reply == 'w':
-			from mmgen.term import do_pager
 			do_pager(gpl.conditions)
 			do_pager(gpl.conditions)
 		elif reply == 'c':
 		elif reply == 'c':
-			msg(""); break
+			msg(''); break
 		else:
 		else:
-			msg_r("\r")
-	msg("")
+			msg_r('\r')
+	msg('')

+ 6 - 15
setup.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
 # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
 #
 #
 # This program is free software: you can redistribute it and/or modify
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # it under the terms of the GNU General Public License as published by
@@ -20,19 +20,20 @@ from distutils.core import setup
 
 
 setup(
 setup(
 		name         = 'mmgen',
 		name         = 'mmgen',
-		description   = 'A complete Bitcoin cold-storage solution for the command line',
-		version      = '0.8.2',
+		description  = 'A complete Bitcoin cold-storage solution for the command line',
+		version      = '0.8.3',
 		author       = 'Philemon',
 		author       = 'Philemon',
 		author_email = 'mmgen-py@yandex.com',
 		author_email = 'mmgen-py@yandex.com',
 		url          = 'https://github.com/mmgen/mmgen',
 		url          = 'https://github.com/mmgen/mmgen',
 		license      = 'GNU GPL v3',
 		license      = 'GNU GPL v3',
 		platforms    = 'Linux, MS Windows',
 		platforms    = 'Linux, MS Windows',
-		keywords     = 'Bitcoin, wallet, cold storage, offline storage, open-source, command-line, Python, Bitcoin Core, bitcoind',
+		keywords     = 'Bitcoin, wallet, cold storage, offline storage, open-source, command-line, Python, Bitcoin Core, bitcoind, hd, deterministic, hierarchical',
 		py_modules = [
 		py_modules = [
 			'mmgen.__init__',
 			'mmgen.__init__',
 			'mmgen.addr',
 			'mmgen.addr',
 			'mmgen.bitcoin',
 			'mmgen.bitcoin',
 			'mmgen.globalvars',
 			'mmgen.globalvars',
+			'mmgen.common',
 			'mmgen.crypto',
 			'mmgen.crypto',
 			'mmgen.filename',
 			'mmgen.filename',
 			'mmgen.license',
 			'mmgen.license',
@@ -40,7 +41,7 @@ setup(
 			'mmgen.mn_tirosh',
 			'mmgen.mn_tirosh',
 			'mmgen.obj',
 			'mmgen.obj',
 			'mmgen.opts',
 			'mmgen.opts',
-			'mmgen.opt',
+			'mmgen.rpc',
 			'mmgen.seed',
 			'mmgen.seed',
 			'mmgen.term',
 			'mmgen.term',
 			'mmgen.test',
 			'mmgen.test',
@@ -51,7 +52,6 @@ setup(
 			'mmgen.main',
 			'mmgen.main',
 			'mmgen.main_addrgen',
 			'mmgen.main_addrgen',
 			'mmgen.main_addrimport',
 			'mmgen.main_addrimport',
-			'mmgen.main_pywallet',
 			'mmgen.main_tool',
 			'mmgen.main_tool',
 			'mmgen.main_txcreate',
 			'mmgen.main_txcreate',
 			'mmgen.main_txsend',
 			'mmgen.main_txsend',
@@ -61,14 +61,6 @@ setup(
 			'mmgen.share.__init__',
 			'mmgen.share.__init__',
 			'mmgen.share.Opts',
 			'mmgen.share.Opts',
 
 
-			'mmgen.rpc.__init__',
-			'mmgen.rpc.config',
-			'mmgen.rpc.connection',
-			'mmgen.rpc.data',
-			'mmgen.rpc.exceptions',
-			'mmgen.rpc.proxy',
-			'mmgen.rpc.util',
-
 			'test.__init__',
 			'test.__init__',
 			'test.test',
 			'test.test',
 			'test.tooltest',
 			'test.tooltest',
@@ -85,7 +77,6 @@ setup(
 			'mmgen-txcreate',
 			'mmgen-txcreate',
 			'mmgen-txsign',
 			'mmgen-txsign',
 			'mmgen-txsend',
 			'mmgen-txsend',
-			'mmgen-pywallet',
 			'mmgen-tool',
 			'mmgen-tool',
 		]
 		]
 	)
 	)

+ 35 - 22
test/gentest.py

@@ -1,8 +1,25 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
+#
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+test/gentest.py:  Bitcoin key/address generation tests for the MMGen suite
+"""
 
 
-# Chdir to repo root.
-# Since script is not in repo root, fix sys.path so that modules are
-# imported from repo, not system.
 import sys,os
 import sys,os
 pn = os.path.dirname(sys.argv[0])
 pn = os.path.dirname(sys.argv[0])
 os.chdir(os.path.join(pn,os.pardir))
 os.chdir(os.path.join(pn,os.pardir))
@@ -10,9 +27,8 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
 
 
 from binascii import hexlify
 from binascii import hexlify
 
 
-import mmgen.opt as opt
-import mmgen.globalvars as g
-from mmgen.util import msg,msg_r,mmsg,mdie,red,green,vmsg,start_mscolor
+# Import these _after_ local path's been added to sys.path
+from mmgen.common import *
 from mmgen.bitcoin import hextowif,privnum2addr
 from mmgen.bitcoin import hextowif,privnum2addr
 
 
 start_mscolor()
 start_mscolor()
@@ -20,7 +36,7 @@ start_mscolor()
 rounds = 100
 rounds = 100
 opts_data = {
 opts_data = {
 	'desc': "Test addresses generated by {} against output of 'keyconv'".format(g.proj_name),
 	'desc': "Test addresses generated by {} against output of 'keyconv'".format(g.proj_name),
-	'usage':"[options] [rounds]",
+	'usage':'[options] [rounds]',
 	'options': """
 	'options': """
 -h, --help         Print this help message
 -h, --help         Print this help message
 -s, --system       Test scripts and modules installed on system rather than
 -s, --system       Test scripts and modules installed on system rather than
@@ -37,45 +53,42 @@ routines, which use the Python ecdsa library.
 rounds is {} by default.
 rounds is {} by default.
 """.format(rounds,pnm=g.proj_name)
 """.format(rounds,pnm=g.proj_name)
 }
 }
-cmd_args = opt.opts.init(opts_data,add_opts=["exact_output"])
+cmd_args = opts.init(opts_data,add_opts=['exact_output'])
 
 
 if len(cmd_args) == 1:
 if len(cmd_args) == 1:
 	try:
 	try:
 		rounds = int(cmd_args[0])
 		rounds = int(cmd_args[0])
 		assert rounds > 0
 		assert rounds > 0
 	except:
 	except:
-		msg("'rounds' must be a positive integer")
-		sys.exit(1)
+		die(1,"'rounds' must be a positive integer")
 
 
 elif len(cmd_args) > 1:
 elif len(cmd_args) > 1:
-	opt.opts.usage(opts_data)
+	opts.usage(opts_data)
 
 
 if opt.system: sys.path.pop(0)
 if opt.system: sys.path.pop(0)
 
 
 from mmgen.addr import test_for_keyconv
 from mmgen.addr import test_for_keyconv
 if not test_for_keyconv(silent=True):
 if not test_for_keyconv(silent=True):
-	msg(
-"To run this test, you must install 'keyconv' from the vanitygen package.")
-	sys.exit(1)
+	die(1,"To run this test, you must install 'keyconv' from the vanitygen package.")
 
 
-msg(green("Comparing {}'s internally generated addresses against output of 'keyconv'").format(g.proj_name))
+m = "Comparing {}'s internally generated addresses against output of 'keyconv'"
+msg(green(m.format(g.proj_name)))
 
 
 from subprocess import check_output
 from subprocess import check_output
 for i in range(1,rounds+1):
 for i in range(1,rounds+1):
-	msg_r("\rRound %s/%s " % (i,rounds))
+	msg_r('\rRound %s/%s ' % (i,rounds))
 	sec = hexlify(os.urandom(32))
 	sec = hexlify(os.urandom(32))
 	wif = hextowif(sec)
 	wif = hextowif(sec)
 	a = privnum2addr(int(sec,16))
 	a = privnum2addr(int(sec,16))
-	vmsg("\nkey:  %s\naddr: %s\n" % (wif,a))
-	b = check_output(["keyconv", wif]).split()[1]
+	vmsg('\nkey:  %s\naddr: %s\n' % (wif,a))
+	b = check_output(['keyconv', wif]).split()[1]
 	if a != b:
 	if a != b:
-		msg_r(red("\nERROR: Addresses do not match!"))
-		msg("""
+		msg_r(red('\nERROR: Addresses do not match!'))
+		die(3,"""
   sec key: {}
   sec key: {}
   WIF key: {}
   WIF key: {}
   {pnm}:   {}
   {pnm}:   {}
   keyconv: {}
   keyconv: {}
 """.format(sec,wif,a,b,pnm=g.proj_name).rstrip())
 """.format(sec,wif,a,b,pnm=g.proj_name).rstrip())
-		sys.exit(3)
 
 
-msg(green("%sOK" % ("" if opt.verbose else "\n")))
+msg(green(('\n','')[bool(opt.verbose)] + 'OK'))

File diff suppressed because it is too large
+ 332 - 319
test/test.py


+ 153 - 104
test/tooltest.py

@@ -1,64 +1,81 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
+#
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
+# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+test/tooltest.py:  Tests for the 'mmgen-tool' utility
+"""
 
 
-# Chdir to repo root.
-# Since script is not in repo root, fix sys.path so that modules are
-# imported from repo, not system.
 import sys,os
 import sys,os
 pn = os.path.dirname(sys.argv[0])
 pn = os.path.dirname(sys.argv[0])
 os.chdir(os.path.join(pn,os.pardir))
 os.chdir(os.path.join(pn,os.pardir))
 sys.path.__setitem__(0,os.path.abspath(os.curdir))
 sys.path.__setitem__(0,os.path.abspath(os.curdir))
 
 
-import mmgen.opt as opt
-from mmgen.util import *
-from collections import OrderedDict
+# Import this _after_ local path's been added to sys.path
+from mmgen.common import *
 
 
 start_mscolor()
 start_mscolor()
 
 
+from collections import OrderedDict
 cmd_data = OrderedDict([
 cmd_data = OrderedDict([
 	('util', {
 	('util', {
-			'desc': "base conversion, hashing and file utilities",
+			'desc': 'base conversion, hashing and file utilities',
 			'cmd_data': OrderedDict([
 			'cmd_data': OrderedDict([
 				('strtob58',     ()),
 				('strtob58',     ()),
-				('b58tostr',     ("strtob58","io")),
+				('b58tostr',     ('strtob58','io')),
 				('hextob58',     ()),
 				('hextob58',     ()),
-				('b58tohex',     ("hextob58","io")),
+				('b58tohex',     ('hextob58','io')),
 				('b58randenc',   ()),
 				('b58randenc',   ()),
 				('hextob32',     ()),
 				('hextob32',     ()),
-				('b32tohex',     ("hextob32","io")),
+				('b32tohex',     ('hextob32','io')),
 				('randhex',      ()),
 				('randhex',      ()),
 				('id8',          ()),
 				('id8',          ()),
 				('id6',          ()),
 				('id6',          ()),
 				('str2id6',      ()),
 				('str2id6',      ()),
-				("sha256x2",     ()),
-				("hexreverse",   ()),
-				("hexlify",      ()),
+				('sha256x2',     ()),
+				('hexreverse',   ()),
+				('hexlify',      ()),
 				('hexdump',      ()),
 				('hexdump',      ()),
-				('unhexdump',    ("hexdump","io")),
+				('unhexdump',    ('hexdump','io')),
 				('rand2file',    ()),
 				('rand2file',    ()),
 			])
 			])
 		}
 		}
 	),
 	),
 	('bitcoin', {
 	('bitcoin', {
-			'desc': "Bitcoin address/key commands",
+			'desc': 'Bitcoin address/key commands',
 			'cmd_data': OrderedDict([
 			'cmd_data': OrderedDict([
 				('randwif',      ()),
 				('randwif',      ()),
 				('randpair',     ()),
 				('randpair',     ()),
-				('wif2addr',     ("randpair","o2")),
-				('wif2hex',      ("randpair","o2")),
-				('privhex2addr', ("wif2hex","o2")), # wif from randpair o2
-				('hex2wif',      ("wif2hex","io2")),
-				('addr2hexaddr', ("randpair","o2")),
-				('hexaddr2addr', ("addr2hexaddr","io2")),
-# ("pubkey2addr",  ['<public key in hex format> [str]']),
-# ("pubkey2hexaddr", ['<public key in hex format> [str]']),
+				('wif2addr',     ('randpair','o2')),
+				('wif2hex',      ('randpair','o2')),
+				('privhex2addr', ('wif2hex','o2')), # wif from randpair o2
+				('hex2wif',      ('wif2hex','io2')),
+				('addr2hexaddr', ('randpair','o2')),
+				('hexaddr2addr', ('addr2hexaddr','io2')),
+# ('pubkey2addr',  ['<public key in hex format> [str]']),
+# ('pubkey2hexaddr', ['<public key in hex format> [str]']),
 			])
 			])
 		}
 		}
 	),
 	),
 	('mnemonic', {
 	('mnemonic', {
-			'desc': "mnemonic commands",
+			'desc': 'mnemonic commands',
 			'cmd_data': OrderedDict([
 			'cmd_data': OrderedDict([
 				('hex2mn',       ()),
 				('hex2mn',       ()),
-				('mn2hex',       ("hex2mn","io3")),
+				('mn2hex',       ('hex2mn','io3')),
 				('mn_rand128',   ()),
 				('mn_rand128',   ()),
 				('mn_rand192',   ()),
 				('mn_rand192',   ()),
 				('mn_rand256',   ()),
 				('mn_rand256',   ()),
@@ -66,19 +83,34 @@ cmd_data = OrderedDict([
 				('mn_printlist', ()),
 				('mn_printlist', ()),
 			])
 			])
 		}
 		}
-	)
+	),
+	('rpc', {
+			'desc': 'Bitcoind RPC commands',
+			'cmd_data': OrderedDict([
+#				('keyaddrfile_chksum', ()), # interactive
+				('addrfile_chksum', ()),
+				('getbalance',      ()),
+				('listaddresses',   ()),
+				('txview',          ()),
+			])
+		}
+	),
 ])
 ])
 
 
 cfg = {
 cfg = {
-	'name':          "the tool utility",
-	'enc_passwd':    "Ten Satoshis",
-	'tmpdir':        "test/tmp10",
+	'name':          'the tool utility',
+	'enc_passwd':    'Ten Satoshis',
+	'tmpdir':        'test/tmp10',
 	'tmpdir_num':    10,
 	'tmpdir_num':    10,
+	'refdir':        'test/ref',
+	'txfile':        'tx_FFB367[1.234].raw',
+	'addrfile':      '98831F3A[1,31-33,500-501,1010-1011].addrs',
+	'addrfile_chk':  '6FEF 6FB9 7B13 5D91 854A 0BD3',
 }
 }
 
 
 opts_data = {
 opts_data = {
 	'desc': "Test suite for the 'mmgen-tool' utility",
 	'desc': "Test suite for the 'mmgen-tool' utility",
-	'usage':"[options] [command]",
+	'usage':'[options] [command]',
 	'options': """
 	'options': """
 -h, --help          Print this help message
 -h, --help          Print this help message
 -l, --list-cmds     List and describe the tests and commands in the test suite
 -l, --list-cmds     List and describe the tests and commands in the test suite
@@ -92,18 +124,18 @@ If no command is given, the whole suite of tests is run.
 """
 """
 }
 }
 
 
-cmd_args = opt.opts.init(opts_data,add_opts=["exact_output"])
+cmd_args = opts.init(opts_data,add_opts=['exact_output'])
 
 
 if opt.system: sys.path.pop(0)
 if opt.system: sys.path.pop(0)
 
 
 if opt.list_cmds:
 if opt.list_cmds:
-	fs = "  {:<{w}} - {}"
-	Msg("Available commands:")
+	fs = '  {:<{w}} - {}'
+	Msg('Available commands:')
 	w = max([len(i) for i in cmd_data])
 	w = max([len(i) for i in cmd_data])
 	for cmd in cmd_data:
 	for cmd in cmd_data:
 		Msg(fs.format(cmd,cmd_data[cmd]['desc'],w=w))
 		Msg(fs.format(cmd,cmd_data[cmd]['desc'],w=w))
-	Msg("\nAvailable utilities:")
-	Msg(fs.format("clean","Clean the tmp directory",w=w))
+	Msg('\nAvailable utilities:')
+	Msg(fs.format('clean','Clean the tmp directory',w=w))
 	sys.exit()
 	sys.exit()
 
 
 import binascii
 import binascii
@@ -119,15 +151,13 @@ class MMGenToolTestSuite(object):
 		fns = []
 		fns = []
 		if cdata:
 		if cdata:
 			name,code = cdata
 			name,code = cdata
-			io,count = code,1
-			if code[-1] in "0123456789":
-				io,count = code[:-1],int(code[-1])
+			io,count = (code[:-1],int(code[-1])) if code[-1] in '0123456789' else (code,1)
 
 
 			for c in range(count):
 			for c in range(count):
-				fns += ["%s%s%s" % (
+				fns += ['%s%s%s' % (
 					name,
 					name,
-					(c+1 if count > 1 else ""),
-					('.in' if ch=='i' else '.out')
+					('',c+1)[count > 1],
+					('.out','.in')[ch=='i']
 				) for ch in io]
 				) for ch in io]
 		return fns
 		return fns
 
 
@@ -150,75 +180,85 @@ class MMGenToolTestSuite(object):
 		self.__class__.__dict__[cmd](*([self,cmd] + file_list))
 		self.__class__.__dict__[cmd](*([self,cmd] + file_list))
 
 
 
 
-	def run_cmd(self,name,tool_args,kwargs="",extra_msg="",silent=False,strip=True):
-		mmgen_tool = "mmgen-tool"
+	def run_cmd(self,name,tool_args,kwargs='',extra_msg='',silent=False,strip=True):
+		mmgen_tool = 'mmgen-tool'
 		if not opt.system:
 		if not opt.system:
 			mmgen_tool = os.path.join(os.curdir,mmgen_tool)
 			mmgen_tool = os.path.join(os.curdir,mmgen_tool)
 
 
-		sys_cmd = ["python", mmgen_tool, "-d",cfg['tmpdir'], name] + tool_args + kwargs.split()
-		if extra_msg: extra_msg = "(%s)" % extra_msg
-		full_name = " ".join([name]+kwargs.split()+extra_msg.split())
+		sys_cmd = ['python', mmgen_tool, '-d',cfg['tmpdir'], name] + tool_args + kwargs.split()
+		if extra_msg: extra_msg = '(%s)' % extra_msg
+		full_name = ' '.join([name]+kwargs.split()+extra_msg.split())
 		if not silent:
 		if not silent:
 			if opt.verbose:
 			if opt.verbose:
-				sys.stderr.write(green("Testing %s\nExecuting " % full_name))
-				sys.stderr.write("%s\n" % cyan(repr(sys_cmd)))
+				sys.stderr.write(green('Testing %s\nExecuting ' % full_name))
+				sys.stderr.write('%s\n' % cyan(repr(sys_cmd)))
 			else:
 			else:
-				msg_r("Testing %-31s%s" % (full_name+":",""))
+				msg_r('Testing %-31s%s' % (full_name+':',''))
 
 
 		import subprocess
 		import subprocess
-		ret = subprocess.check_output(sys_cmd)
-		return (ret,ret.rstrip())[int(strip)]
-
-	def run_cmd_chk(self,name,f1,f2,kwargs="",extra_msg=""):
+		p = subprocess.Popen(
+			sys_cmd,
+			stdout=subprocess.PIPE,
+			stderr=subprocess.PIPE,
+			)
+		a,b = p.communicate()
+		retcode = p.wait()
+		if retcode != 0:
+			msg('%s\n%s\n%s'%(red('FAILED'),yellow('Command stderr output:'),b))
+			die(1,red('Called process returned with an error (retcode %s)' % retcode))
+		return (a,a.rstrip())[bool(strip)]
+
+	def run_cmd_chk(self,name,f1,f2,kwargs='',extra_msg=''):
 		idata = read_from_file(f1).rstrip()
 		idata = read_from_file(f1).rstrip()
 		odata = read_from_file(f2).rstrip()
 		odata = read_from_file(f2).rstrip()
 		ret = self.run_cmd(name,[odata],kwargs=kwargs,extra_msg=extra_msg)
 		ret = self.run_cmd(name,[odata],kwargs=kwargs,extra_msg=extra_msg)
-		vmsg("In:   " + repr(odata))
-		vmsg("Out:  " + repr(ret))
+		vmsg('In:   ' + repr(odata))
+		vmsg('Out:  ' + repr(ret))
 		if ret == idata: ok()
 		if ret == idata: ok()
 		else:
 		else:
-			msg(red(
+			die(3,red(
 	"Error: values don't match:\nIn:  %s\nOut: %s" % (repr(idata),repr(ret))))
 	"Error: values don't match:\nIn:  %s\nOut: %s" % (repr(idata),repr(ret))))
-			sys.exit(3)
 		return ret
 		return ret
 
 
-	def run_cmd_nochk(self,name,f1,kwargs=""):
+	def run_cmd_nochk(self,name,f1,kwargs=''):
 		odata = read_from_file(f1).rstrip()
 		odata = read_from_file(f1).rstrip()
 		ret = self.run_cmd(name,[odata],kwargs=kwargs)
 		ret = self.run_cmd(name,[odata],kwargs=kwargs)
-		vmsg("In:   " + repr(odata))
-		vmsg("Out:  " + repr(ret))
+		vmsg('In:   ' + repr(odata))
+		vmsg('Out:  ' + repr(ret))
 		return ret
 		return ret
 
 
-	def run_cmd_out(self,name,carg=None,Return=False,kwargs="",fn_idx="",extra_msg=""):
-		if carg: write_to_tmpfile(cfg,"%s%s.in" % (name,fn_idx),carg+"\n")
-		ret = self.run_cmd(name,[carg] if carg else [],kwargs=kwargs,extra_msg=extra_msg)
-		if carg: vmsg("In:   " + repr(carg))
-		vmsg("Out:  " + repr(ret))
+	def run_cmd_out(self,name,carg=None,Return=False,kwargs='',fn_idx='',extra_msg='',literal=False,chkdata=''):
+		if carg: write_to_tmpfile(cfg,'%s%s.in' % (name,fn_idx),carg+'\n')
+		ret = self.run_cmd(name,([],[carg])[bool(carg)],kwargs=kwargs,extra_msg=extra_msg)
+		if carg: vmsg('In:   ' + repr(carg))
+		vmsg('Out:  ' + (repr(ret),ret)[literal])
 		if ret:
 		if ret:
-			write_to_tmpfile(cfg,"%s%s.out" % (name,fn_idx),ret+"\n")
+			write_to_tmpfile(cfg,'%s%s.out' % (name,fn_idx),ret+'\n')
+			if chkdata:
+				cmp_or_die(ret,chkdata)
+				return
 			if Return: return ret
 			if Return: return ret
 			else:   ok()
 			else:   ok()
 		else:
 		else:
-			msg(red("Error for command '%s'" % name))
-			sys.exit(3)
+			die(3,red("Error for command '%s'" % name))
 
 
 	def run_cmd_randinput(self,name,strip=True):
 	def run_cmd_randinput(self,name,strip=True):
 		s = os.urandom(128)
 		s = os.urandom(128)
-		fn = name+".in"
+		fn = name+'.in'
 		write_to_tmpfile(cfg,fn,s,binary=True)
 		write_to_tmpfile(cfg,fn,s,binary=True)
 		ret = self.run_cmd(name,[get_tmpfile_fn(cfg,fn)],strip=strip)
 		ret = self.run_cmd(name,[get_tmpfile_fn(cfg,fn)],strip=strip)
-		fn = name+".out"
-		write_to_tmpfile(cfg,fn,ret+"\n")
+		fn = name+'.out'
+		write_to_tmpfile(cfg,fn,ret+'\n')
 		ok()
 		ok()
-		vmsg("Returned: %s" % ret)
+		vmsg('Returned: %s' % ret)
 
 
 	def str2id6(self,name):
 	def str2id6(self,name):
 		s = getrandstr(120,no_space=True)
 		s = getrandstr(120,no_space=True)
-		s2 = " %s %s %s %s %s " % (s[:3],s[3:9],s[9:29],s[29:50],s[50:120])
-		ret1 = self.run_cmd(name,[s],extra_msg="unspaced input"); ok()
-		ret2 = self.run_cmd(name,[s2],extra_msg="spaced input")
+		s2 = ' %s %s %s %s %s ' % (s[:3],s[3:9],s[9:29],s[29:50],s[50:120])
+		ret1 = self.run_cmd(name,[s],extra_msg='unspaced input'); ok()
+		ret2 = self.run_cmd(name,[s2],extra_msg='spaced input')
 		cmp_or_die(ret1,ret2)
 		cmp_or_die(ret1,ret2)
-		vmsg("Returned: %s" % ret1)
+		vmsg('Returned: %s' % ret1)
 
 
 	def mn_rand128(self,name):
 	def mn_rand128(self,name):
 		self.run_cmd_out(name)
 		self.run_cmd_out(name)
@@ -246,9 +286,9 @@ class MMGenToolTestSuite(object):
 		cmp_or_die(orig,ret)
 		cmp_or_die(orig,ret)
 
 
 	def rand2file(self,name):
 	def rand2file(self,name):
-		of = name + ".out"
+		of = name + '.out'
 		dlen = 1024
 		dlen = 1024
-		self.run_cmd(name,[of,str(1024),"threads=4","silent=1"],strip=False)
+		self.run_cmd(name,[of,str(1024),'threads=4','silent=1'],strip=False)
 		d = read_from_tmpfile(cfg,of,binary=True)
 		d = read_from_tmpfile(cfg,of,binary=True)
 		cmp_or_die(dlen,len(d))
 		cmp_or_die(dlen,len(d))
 
 
@@ -263,52 +303,63 @@ class MMGenToolTestSuite(object):
 	def b32tohex(self,name,f1,f2): self.run_cmd_chk(name,f1,f2)
 	def b32tohex(self,name,f1,f2): self.run_cmd_chk(name,f1,f2)
 	def b58randenc(self,name):
 	def b58randenc(self,name):
 		ret = self.run_cmd_out(name,Return=True)
 		ret = self.run_cmd_out(name,Return=True)
-		ok_or_die(ret,is_b58_str,"base 58 string")
+		ok_or_die(ret,is_b58_str,'base 58 string')
 	def randhex(self,name):
 	def randhex(self,name):
 		ret = self.run_cmd_out(name,Return=True)
 		ret = self.run_cmd_out(name,Return=True)
-		ok_or_die(ret,binascii.unhexlify,"hex string")
+		ok_or_die(ret,binascii.unhexlify,'hex string')
 	def randwif(self,name):
 	def randwif(self,name):
-		for n,k in enumerate(["","compressed=1"]):
+		for n,k in enumerate(['','compressed=1']):
 			ret = self.run_cmd_out(name,kwargs=k,Return=True,fn_idx=n+1)
 			ret = self.run_cmd_out(name,kwargs=k,Return=True,fn_idx=n+1)
-			ok_or_die(ret,is_wif,"WIF key")
+			ok_or_die(ret,is_wif,'WIF key')
 	def randpair(self,name):
 	def randpair(self,name):
-		for n,k in enumerate(["","compressed=1"]):
+		for n,k in enumerate(['','compressed=1']):
 			wif,addr = self.run_cmd_out(name,kwargs=k,Return=True,fn_idx=n+1).split()
 			wif,addr = self.run_cmd_out(name,kwargs=k,Return=True,fn_idx=n+1).split()
-			ok_or_die(wif,is_wif,"WIF key",skip_ok=True)
-			ok_or_die(addr,is_btc_addr,"Bitcoin address")
+			ok_or_die(wif,is_wif,'WIF key',skip_ok=True)
+			ok_or_die(addr,is_btc_addr,'Bitcoin address')
 	def hex2wif(self,name,f1,f2,f3,f4):
 	def hex2wif(self,name,f1,f2,f3,f4):
-		for n,fi,fo,k in (1,f1,f2,""),(2,f3,f4,"compressed=1"):
+		for n,fi,fo,k in (1,f1,f2,''),(2,f3,f4,'compressed=1'):
 			ret = self.run_cmd_chk(name,fi,fo,kwargs=k)
 			ret = self.run_cmd_chk(name,fi,fo,kwargs=k)
 	def wif2hex(self,name,f1,f2):
 	def wif2hex(self,name,f1,f2):
-		for n,f,k in (1,f1,""),(2,f2,"compressed=1"):
+		for n,f,k in (1,f1,''),(2,f2,'compressed=1'):
 			wif = read_from_file(f).split()[0]
 			wif = read_from_file(f).split()[0]
 			self.run_cmd_out(name,wif,kwargs=k,fn_idx=n)
 			self.run_cmd_out(name,wif,kwargs=k,fn_idx=n)
 	def wif2addr(self,name,f1,f2):
 	def wif2addr(self,name,f1,f2):
-		for n,f,k in (1,f1,""),(2,f2,"compressed=1"):
+		for n,f,k in (1,f1,''),(2,f2,'compressed=1'):
 			wif = read_from_file(f).split()[0]
 			wif = read_from_file(f).split()[0]
 			self.run_cmd_out(name,wif,kwargs=k,fn_idx=n)
 			self.run_cmd_out(name,wif,kwargs=k,fn_idx=n)
 	def addr2hexaddr(self,name,f1,f2):
 	def addr2hexaddr(self,name,f1,f2):
-		for n,f,m in (1,f1,""),(2,f2,"from compressed"):
+		for n,f,m in (1,f1,''),(2,f2,'from compressed'):
 			addr = read_from_file(f).split()[-1]
 			addr = read_from_file(f).split()[-1]
 			self.run_cmd_out(name,addr,fn_idx=n,extra_msg=m)
 			self.run_cmd_out(name,addr,fn_idx=n,extra_msg=m)
 	def hexaddr2addr(self,name,f1,f2,f3,f4):
 	def hexaddr2addr(self,name,f1,f2,f3,f4):
-		for n,fi,fo,m in (1,f1,f2,""),(2,f3,f4,"from compressed"):
+		for n,fi,fo,m in (1,f1,f2,''),(2,f3,f4,'from compressed'):
 			self.run_cmd_chk(name,fi,fo,extra_msg=m)
 			self.run_cmd_chk(name,fi,fo,extra_msg=m)
 	def privhex2addr(self,name,f1,f2):
 	def privhex2addr(self,name,f1,f2):
 		key1 = read_from_file(f1).rstrip()
 		key1 = read_from_file(f1).rstrip()
 		key2 = read_from_file(f2).rstrip()
 		key2 = read_from_file(f2).rstrip()
-		for n,args in enumerate([[key1],[key2,"compressed=1"]]):
+		for n,args in enumerate([[key1],[key2,'compressed=1']]):
 			ret = self.run_cmd(name,args).rstrip()
 			ret = self.run_cmd(name,args).rstrip()
-			iaddr = read_from_tmpfile(cfg,"randpair%s.out" % (n+1)).split()[-1]
+			iaddr = read_from_tmpfile(cfg,'randpair%s.out' % (n+1)).split()[-1]
 			cmp_or_die(iaddr,ret)
 			cmp_or_die(iaddr,ret)
 	def hex2mn(self,name):
 	def hex2mn(self,name):
-		for n,size,m in(1,16,"128-bit"),(2,24,"192-bit"),(3,32,"256-bit"):
+		for n,size,m in(1,16,'128-bit'),(2,24,'192-bit'),(3,32,'256-bit'):
 			hexnum = getrandhex(size)
 			hexnum = getrandhex(size)
 			self.run_cmd_out(name,hexnum,fn_idx=n,extra_msg=m)
 			self.run_cmd_out(name,hexnum,fn_idx=n,extra_msg=m)
 	def mn2hex(self,name,f1,f2,f3,f4,f5,f6):
 	def mn2hex(self,name,f1,f2,f3,f4,f5,f6):
-		for f_i,f_o,m in (f1,f2,"128-bit"),(f3,f4,"192-bit"),(f5,f6,"256-bit"):
+		for f_i,f_o,m in (f1,f2,'128-bit'),(f3,f4,'192-bit'),(f5,f6,'256-bit'):
 			self.run_cmd_chk(name,f_i,f_o,extra_msg=m)
 			self.run_cmd_chk(name,f_i,f_o,extra_msg=m)
 
 
+	def getbalance(self,name):
+		self.run_cmd_out(name,literal=True)
+	def listaddresses(self,name):
+		self.run_cmd_out(name,literal=True)
+	def txview(self,name):
+		fn = os.path.join(cfg['refdir'],cfg['txfile'])
+		self.run_cmd_out(name,fn,literal=True)
+	def addrfile_chksum(self,name):
+		fn = os.path.join(cfg['refdir'],cfg['addrfile'])
+		self.run_cmd_out(name,fn,literal=True,chkdata=cfg['addrfile_chk'])
+
 # main()
 # main()
 import time
 import time
 start_time = int(time.time())
 start_time = int(time.time())
@@ -317,26 +368,24 @@ mk_tmpdir(cfg)
 
 
 if cmd_args:
 if cmd_args:
 	if len(cmd_args) != 1:
 	if len(cmd_args) != 1:
-		msg("Only one command may be specified")
-		sys.exit(1)
+		die(1,'Only one command may be specified')
 
 
 	cmd = cmd_args[0]
 	cmd = cmd_args[0]
 	if cmd in cmd_data:
 	if cmd in cmd_data:
-		msg("Running tests for %s:" % cmd_data[cmd]['desc'])
+		msg('Running tests for %s:' % cmd_data[cmd]['desc'])
 		ts.do_cmds(cmd)
 		ts.do_cmds(cmd)
-	elif cmd == "clean":
+	elif cmd == 'clean':
 		cleandir(cfg['tmpdir'])
 		cleandir(cfg['tmpdir'])
-		sys.exit(0)
+		sys.exit()
 	else:
 	else:
-		msg("'%s': unrecognized command" % cmd)
-		sys.exit(1)
+		die(1,"'%s': unrecognized command" % cmd)
 else:
 else:
 	cleandir(cfg['tmpdir'])
 	cleandir(cfg['tmpdir'])
 	for cmd in cmd_data:
 	for cmd in cmd_data:
-		msg("Running tests for %s:" % cmd_data[cmd]['desc'])
+		msg('Running tests for %s:' % cmd_data[cmd]['desc'])
 		ts.do_cmds(cmd)
 		ts.do_cmds(cmd)
-		if cmd is not cmd_data.keys()[-1]: msg("")
+		if cmd is not cmd_data.keys()[-1]: msg('')
 
 
 t = int(time.time()) - start_time
 t = int(time.time()) - start_time
 msg(green(
 msg(green(
-	"All requested tests finished OK, elapsed time: %02i:%02i" % (t/60,t%60)))
+	'All requested tests finished OK, elapsed time: %02i:%02i' % (t/60,t%60)))

Some files were not shown because too many files changed in this diff