diff --git a/README.md b/README.md
index eb15c3ce..72086597 100644
--- a/README.md
+++ b/README.md
@@ -1,261 +1,436 @@
-# mmgen = Multi-Mode GENerator
-## a Bitcoin cold storage solution for the command line
+MMGen = Multi-Mode GENerator
+============================
-NOTE: Parts of this README are now **out of date**. In particular, the
-new transaction scripts automate the process of offline signing, so that
-your private keys never touch the online machine. An updated README is
-on the way. For the time being, consult the `--help` option of the
-`mmgen-tx*` scripts.
+a Bitcoin cold storage solution for the command line
+----------------------------------------------------
-NOTE: For the time being, MMGen should be considered experimental software.
-Downloading and testing it out is easy, risk-free and encouraged.
-However, spending significant amounts of BTC into your mmgen-generated
-addresses is done at your own risk.
+### Description
-### Features:
+MMGen is implemented as a suite of lightweight Python command-line
+scripts that require only a bare minimum of system resources.
+The scripts function in tandem with a modified
+bitcoind running on an online computer and a standard
+bitcoind running offline to provide a robust solution for securely
+storing, tracking, spending and receiving your Bitcoins. "Non-MMGen"
+addresses can be tracked and spent as well, creating an easy migration
+path from other wallets.
-> As with all deterministic wallets, mmgen can generate an unlimited number
-> of address/key pairs from a single seed. You back up your wallet only once.
+The online bitcoin daemon is modified to support watch-only addresses,
+a feature soon to be included in the mainline Satoshi build.
+In the meantime, instructions are provided below for compiling the
+watch-only bitcoind, which under Linux is surprisingly easy.
-> With MMGen you can choose from four different ways to access your Bitcoins:
+MMGen uses the reference Satoshi daemon, rather than less-reliable
+third-party software, to do all the "heavy lifting" of tracking and
+signing transactions.
+And unlike other online/offline wallet solutions, the MMGen system is
+completely self-contained, relying on no external server to do its
+work; no third party will know which addresses you're tracking.
->> 1) an encrypted wallet (the AES 256 key is generated from your
->> password using the crack-resistant scrypt hash function. The
->> wallet's password and hash strength can be changed);
+Like all deterministic wallets, MMGen can generate a virtually
+unlimited number of address/key pairs from a single seed. Your wallet
+never changes, so you need back it up only once.
+Transactions are signed offline: your private keys never touch an
+online computer.
->> 2) a short, human-readable seed file (unencrypted);
+At the heart of the MMGen system is the seed, the "master key"
+providing access to all your Bitcoins. The seed can be stored in four
+different ways:
->> 3) an Electrum-like mnemonic of 12, 18 or 24 words; or
+> 1) as a wallet with a password
+> encrypted using the crack-resistant scrypt hash function.
+> Scrypt's parameters can be adjusted on the command line, making your
+> wallet's password virtually impossible to crack should it fall into the wrong
+> hands. The wallet is a tiny text file
+> suitable for printing or even writing out by hand;
->> 4) a brainwallet password (recommended for expert users only).
+> 2) as a seed file: a one-line base-58 representation of your
+> unencrypted seed plus a checksum;
-> Furthermore, these methods can all be combined. If you forget your
-> mnemonic, for example, you can regenerate it and your keys from a
-> stored wallet or seed. Correspondingly, a lost wallet or seed can be
-> recovered from the mnemonic.
+> 3) as an Electrum-like mnemonic of 12, 18 or 24 words; or
-> The wallet and seed are short, simple text files suitable for printing
-> or even writing out by hand. Built-in checksums are used to verify
-> they've been correctly copied. The base-58-encoded seed is short
-> enough to memorize, providing another brain storage alternative.
+> 4) as a brainwallet password (this option is recommended for expert
+> users only).
-> Implemented as a suite of lightweight Python scripts with a
-> command-line interface, MMGen demands practically no system resources.
-> Yet in tandem with a bitcoind enabled for watch-only addresses
-> (see below), it provides a complete solution for securely
-> storing Bitcoins offline and tracking and spending them online.
+The best part is that all these methods can be combined. If you
+forget your mnemonic, for example, you can regenerate it and your keys
+from the stored wallet or seed file. Correspondingly, a lost wallet can
+be regenerated from the mnemonic or seed or a lost seed from the wallet or
+mnemonic.
-### Instructions for Linux/Unix:
+### Download/Install
-### Download/Install:
-> Install required Python modules:
+> #### [Install on Microsoft Windows](doc/MMGenWindowsInstall.md)
- sudo pip install ecdsa scrypt pycrypto bitcoin-python
-
-> Install mmgen:
-
- git clone https://github.com/mmgen/mmgen.git
- cd mmgen; sudo ./setup.py install
-
-> Install vanitygen (optional but recommended):
-
- git clone https://github.com/samr7/vanitygen.git
- (build and put the 'keyconv' executable in your path)
-
-### Getting Started:
-> On your offline computer:
-
-> Generate a wallet with a random seed:
-
- $ mmgen-walletgen
- ...
- Wallet saved to file '89ABCDEF-76543210[256,3].dat'
-
-> "89ABCDEF" is the Seed ID; "76543210" is the Key ID. These are
-> randomly generated, so your IDs will naturally be different than the
-> fictitious ones used in this example.
-
-> The Seed ID never changes and will be used to identify all
-> keys/addresses generated by this wallet. The Key ID changes when the
-> wallet's password or hash preset are changed.
-
-> "256" is the seed length; "3" is the scrypt hash preset. These are
-> configurable.
+> #### [Install on Debian/Ubuntu Linux](doc/MMGenLinuxInstall.md)
-> Generate ten addresses with the wallet:
+### Using MMGen
- $ mmgen-addrgen 89ABCDEF-76543210[256,3].dat 1-10
- ...
- Address data saved to file '89ABCDEF[1-10].addrs'
+#### Generate a wallet (offline computer):
+On your offline computer, generate a wallet with a random seed:
-> Note that the address range, "1-10", is included in the resulting filename.
+ $ mmgen-walletgen
+ ...
+ Wallet saved to file '89ABCDEF-76543210[256,3].mmdat'
- $ cat '89ABCDEF[1-10].addrs'
- 89ABCDEF {
- 1 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
- 2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc
- 3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N
- 4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s
- 5 1PeI55vtp2bX2uKDkAAR2c6ekHNYe4Hcq7
- 6 1FEqfEsSILwXPfMvVvVuUovzTaaST62Mnf
- 7 1LTTzuhMqPLwQ4IGCwwugny6ZMtUQJSJ1
- 8 1F9495H8EJLb54wirgZkVgI47SP7M2RQWv
- 9 1JbrCyt7BdxRE9GX1N7GiEct8UnIjPmpYd
- 10 1H7vVTk4ejUbQXw45I6g5qvPBSe9bsjDqh
- }
+"89ABCDEF" is the Seed ID; "76543210" is the Key ID. These are
+randomly generated, so your IDs will of course be different than the
+fictitious ones used here.
+The Seed ID never changes and will be used to identify all keys/addresses
+generated by this seed. The Key ID changes when the wallet's password
+or hash preset are changed.
-> To store your Bitcoins, spend them into these addresses from whatever
-> wallets/software you're currently using. If you have lots of BTC,
-> generate many addresses so that each address will have only a
-> relatively small balance.
+"256" is the seed length; "3" is the scrypt hash preset. These values
+are configurable: type `mmgen-walletgen --help` for details.
-### Spending your stored coins:
-> Take address 1 out of cold storage by generating a key for it:
+#### Generate addresses (offline computer):
- $ mmgen-keygen 89ABCDEF-76543210[256,3].dat 1
- ...
- Key data saved to file '89ABCDEF[1].akeys'
+Now generate ten addresses with your just-created wallet:
- $ cat 89ABCDEF[1].akeys
- 89ABCDEF {
- 1 sec: 5JCAfK1pjRoJgmpmd2HEMNwHxAzprGIXeQt8dz5qt3iLvU2KCbS
- addr: 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
- }
+ $ mmgen-addrgen 89ABCDEF-76543210[256,3].mmdat 1-10
+ ...
+ Address data saved to file '89ABCDEF[1-10].addrs'
+ $ cat '89ABCDEF[1-10].addrs'
+ 89ABCDEF {
+ 1 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
+ 2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc
+ 3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N
+ 4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s
+ 5 1PeI55vtp2bX2uKDkAAR2c6ekHNYe4Hcq7
+ 6 1FEqfEsSILwXPfMvVvVuUovzTaaST62Mnf
+ 7 1LTTzuhMqPLwQ4IGCwwugny6ZMtUQJSJ1
+ 8 1F9495H8EJLb54wirgZkVgI47SP7M2RQWv
+ 9 1JbrCyt7BdxRE9GX1N7GiEct8UnIjPmpYd
+ 10 1H7vVTk4ejUbQXw45I6g5qvPBSe9bsjDqh
+ }
-> Save the .akeys file to a USB stick and transfer it to your online computer.
+Note that the address range, "1-10", is reflected in the resulting filename.
+MMGen addresses are identified by their seed ID plus number, separated
+by a colon. In this example, "89ABCDEF:1" is the MMGen equivalent
+of Bitcoin address 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE, "89ABCDEF:2"
+the equivalent of 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc, and so forth.
-> On your online computer, import the secret key into a running bitcoind
-> or bitcoin-qt:
+Let's say you've decided to transfer some BTC into the first four
+addresses above. Your first step, then, will be to import these
+addresses into the tracking wallet on your online machine.
+You've chosen to provide the addresses with the labels "Donations",
+"Storage 1", "Storage 2" and "Storage 3" for convenient identification.
- $ bitcoind importprivkey 5JCAfK1pjRoJgmpmd2HEMNwHxAzprGIXeQt8dz5qt3iLvU2KCbS
+Make a copy of the file:
-> You're done! This address' balance can now be spent.
+ $ cp '89ABCDEF[1-10].addrs' my_addrs_for_import
-> OPTIONAL: To track balances without exposing secret keys on your
-> online computer, download and compile sipa's bitcoind patched for
-> watch-only addresses:
+and edit the copy using your favorite text editor. The result will
+look something like this:
- $ git clone https://github.com/sipa/bitcoin
- $ git branch mywatchonly remotes/origin/watchonly
- $ git checkout mywatchonly
- (build, install)
- (You may have to install libboost-all-dev for the build to succeed)
+ $ cat my_addrs_for_import
+ 89ABCDEF {
+ 1 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE Donations
+ 2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc Storage 1
+ 3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N Storage 2
+ 4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s Storage 3
+ }
-> With your newly-compiled bitcoind running, import the addresses from
-> '89ABCDEF[1-10].addrs' to track their balances:
+Note that rows in the list may be arranged in any order; addresses
+need not be consecutive.
- $ bitcoind importaddress 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
- $ bitcoind importaddress 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc
- $ ...
+Copy this file onto a USB stick and transfer it to your online
+computer.
-### Using the mnemonic and seed features:
+#### Import addresses (online computer):
-> Continuing our example above,
+On your online computer, start bitcoind and import the addresses into
+the tracking wallet:
-> Generate a mnemonic from the wallet:
+ $ mmgen-addrimport my_addrs_for_import
- $ mmgen-walletchk -m '89ABCDEF-76543210[256,3].dat'
- ...
- Mnemonic data saved to file '89ABCDEF.words'
+These addresses are now being "tracked". Any BTC transferred
+to them will show up in our listing of unspent outputs.
- $ cat 89ABCDEF.words
- pleasure tumble spider laughter many stumble secret bother
- after search float absent path strong curtain savior
- worst suspend bright touch away dirty measure thorn
+You'll want to track your existing addresses with balances too.
+Make a plain list of these addresses, one address per line, and import
+the list into the tracking wallet using the same command
+with the `'-l'` option:
-> Note: a 128- or 192-bit seed will generate a shorter mnemonic of 12 or
-> 18 words. You may generate a wallet with a these seed lengths by
-> using the `-l` option of `mmgen-walletgen`. Whether you consider
-> 128 bits of entropy enough is your call. It's probably adequate for
-> the foreseeable future.
+ $ mmgen-addrimport -l my_existing_addrs_with_balances
-> Generate addresses 1-11 using the mnemonic instead of the wallet:
+Since the importing process is slow, you may want to do it in stages,
+a few addresses at a time.
- $ mmgen-addrgen -m 89ABCDEF.words 1-11
- ...
- Address data saved to file '89ABCDEF[1-11].addrs'
+For your convenience, comments beginning with a '#' symbol may be
+included in address lists.
+Continue in this fashion until you've imported all addresses with
+balances into your tracking wallet.
-> Compare the first ten addresses with those earlier generated from the
-> wallet. You'll see they're the same.
+#### Create a transaction (online computer):
-> Recover a lost wallet using the mnemonic:
+Now that your existing addresses are imported, you're ready to create
+a test transaction using the `mmgen-txcreate` command. Note that
+transactions are harmless until they're signed and broadcast to the
+network, so feel free to experiment with different transactions
+using different combinations of inputs and outputs.
- $ mmgen-walletgen -m 89ABCDEF.words
- ...
- Wallet saved to file '89ABCDEF-01234567[256,3].dat'
+First of all, you'll probably want to examine your balances:
-> Note that the regenerated wallet has a different Key ID but
-> of course the same Seed ID.
+ $ mmgen-txcreate -i
-> Seeds are generated and input the same way as mnemonics. Just change
-> the `-m` option to `-s` in the preceding commands.
+A list of all your unspent outputs will appear,
+along with a menu allowing you to sort the outputs by four
+criteria: transaction ID, address, amount and transaction age.
+Your overall balance in BTC appears at the top of the screen. The
+list may be viewed in a pager or printed to file. If you have
+ten unspent outputs, your display will look something like this:
-> A seed file for a 256-bit seed looks like this:
+ UNSPENT OUTPUTS (sort order: reverse amount) Total BTC: 39.72
+ Num TX id Vout Address Amount (BTC) Age(days)
+ 1) 04f97185... 2 1F93Znz8PI5Pnvv8ZAJsb74EzKpmRMLFbk 10 320
+ 2) dd900544... 1 194Fceqx86jqIWumphUmfVyFMjAAbMLcSE 9.9287435 7
+ 3) 7ec81a8f... 0 1FhIkRabPSZhhUsA6qvukmfK4T4PZLbC4M 7.26 17
+ 4) 64094b55... 0 16JSUJdGMbxUBEQatAR5sGE89tbSIsLHqg 3.15 140
+ 5) fd687c65... 1 1QKAtU66aUntCBx9m6TfEIf3gQuCNWCVDY 3.15 140
+ 6) 9a8f20e2... 1 1FMNDFz1yUywjJSprjvYY9t1yxkE8GGIwT 3.15 140
+ 7) 03a7c51a... 3 1svxnSdKVIcMs6qWYA7qLzA29orXbzXUm 1.6382466 54
+ 8) 9955f06c... 2 18nWPLQGUzI7X1Rcm4zmVV6Z3xhokdYx9G 1.2 27
+ 9) 8a4ab4f5... 0 13S9HNu7PQn1aJ4qILfhqRSakXwvSTnbwJ 0.23033 3
+ 10) 5bfe5621... 1 1FV1Lhs6Dnc9gMxjJTo6h4nTeIjJbQ1PgV 0.01 42
- $ cat 8B7392ED.mmseed
- f4c84b C5ZT wWpT Jsoi wRVw 2dm9 Aftd WLb8 FggQ eC8h Szjd da9L
+ Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr
+ View options: [g]roup, show [m]mgen addr
+ (Type 'q' to quit sorting, 'p' to print to file, 'v' to view in pager):
-> And for a 128-bit seed:
+Now let's actually create a transaction. Let's say you've decided to
+gradually begin spending your 39.72 BTC balance into your shiny new
+MMGen wallet with seed ID 89ABCDEF.
- $ cat 8E0DFB78.mmseed
- 0fe02f XnyC NfPH piuW dQ2d nM47 VU
+Before doing anything else, you should back up your MMGen wallet in
+several places and possibly on several media too: paper, flash memory
+or CD-ROM, for example. Of course the wallet should have a
+passphrase. Otherwise, anyone who gains physical control of one of
+your backups can easily steal your coins.
-> The latter is short enough to be memorized or written down.
+Recall that there's no limit to the number of addresses you can
+generate with your seed. You've wisely determined that having many
+addresses with relatively small balances is a Good Idea.
+You've decided to begin by breaking up your
+address with the largest balance, 10 BTC, into three roughly equal parts,
+sending it to the addresses labeled "Storage 1", "Storage 2" and
+"Storage 3" (89ABCDEF:1, 89ABCDEF:2 and 89ABCDEF:3).
-> The first word in the seed file is a checksum.
-> To check that you've written or memorized the seed correctly (should
-> you choose to do so), compare it with the first 6 characters of a
-> sha256 hash of the remainder of the line (with spaces removed).
+To refresh our memory, here are the three addresses in question:
+
+ 89ABCDEF {
+ 2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc Storage 1
+ 3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N Storage 2
+ 4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s Storage 3
+ }
+
+The following command will send 3.3 BTC to the first two addresses and
+the remainder of the transaction's inputs to the third, subtracting a
+default transaction fee of 0.001 BTC:
+
+ $ mmgen-txcreate 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc,3.3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N,3.3 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s
+
+The third address has the amount left out, making it a **change
+address**.
+MMGen will compute the change amount (3.399 BTC in this case) automatically.
+
+Alternatively, and more conveniently, you can write the
+addresses in MMGen format:
+
+ $ mmgen-txcreate -a addrs 89ABCDEF:2,3.3 89ABCDEF:3,3.3 89ABCDEF:4
+
+'addrs' is an MMGen address file containing the requested output
+addresses. Any unneeded addresses in the file will be ignored.
+
+Now hit ENTER, choose the
+transaction's input from the list (10 BTC, address
+1F9495H8EJLb54wirgZkVgI47SP7M2RQWv, txid 04f97185...,2),
+and confirm.
+If all goes well, the command will exit with a message like this:
+
+ Transaction data saved to file 'tx_1EDCBA[6.6].raw'
+
+Note that the transaction has a unique ID, and the non-change output
+amount, 6.6 BTC, is conveniently included in the filename.
+
+#### Sign the transaction (offline computer):
+
+Now copy the raw transaction you've just created to a USB stick and
+transfer it to your offline computer for signing. You need to find
+the key for your transaction's one input address,
+1F9495H8EJLb54wirgZkVgI47SP7M2RQWv. If the key in question is in a
+bitcoin 'wallet.dat', there's an included command that will
+conveniently extract the key for you:
+
+ $ mmgen-pywallet -k wallet.dat
+ ...
+ wallet.dat secret keys saved to file wd_EDBC983A[102].keys
+
+You've in fact extracted a list of all of the wallet's 102 keys here,
+but that's not a problem, as the unused keys will be ignored.
+Now go ahead and sign the transaction using this list of keys.
+
+ $ mmgen-txsign -k wd_EDBC983A[102].keys tx_1EDCBA[6.6].raw
+ ...
+ Signed transaction saved to file tx_1EDCBA[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
+dump all their keys and concatenate them into a single file which you
+can use to sign all your future transactions involving wallet.dat
+inputs:
+
+ $ mmgen-pywallet -k wallet1.dat
+ $ mmgen-pywallet -k wallet2.dat
+ $ mmgen-pywallet -k wallet3.dat
+ $ cat wd_*.keys > all_keys
+
+For transactions whose inputs are MMGen addresses, an MMGen
+seed source (i.e. wallet, mnemonic or seed file) is listed on the
+command line after the transaction file, and the required keys are
+automatically generated:
+
+ $ mmgen-txsign tx_9D2C3A[1.23].raw B73B58EA-125FB230[256,3].mmdat
+
+Transactions may contain a mixture of MMGen and non-MMGen inputs as
+well as inputs with more than one MMGen seed ID. Just provide a seed
+source for each seed ID on the command line.
+
+Eventually, when you've placed all your BTC under MMGen control,
+you'll never have to deal with keys again. MMGen just generates them
+on the fly as the need arises.
+
+#### Send the transaction (online computer):
+
+Now we're ready for the final step: broadcasting the transaction to
+the network. Copy the \*.sig file to your online computer, start
+bitcoind, if it's not running, and execute the command
+
+ $ mmgen-txsend tx_1EDCBA[6.6].sig
+
+Like all mmgen commands, `mmgen-txsend` is interactive, so you'll be
+asked for confirmation before the transaction is actually sent.
+
+Once the transaction's confirmed by the network, your three new MMGen
+addresses will appear on the listing of `mmgen-txcreate -i`. Type
+'m' at the menu and they'll be displayed in MMGen format.
+
+Congratulations! You've performed your first MMGen transaction.
+
+### Additional Features
+
+#### Using the mnemonic and seed features:
+
+Continuing our example above, generate a mnemonic from the wallet:
+
+ $ mmgen-walletchk -m '89ABCDEF-76543210[256,3].mmdat'
+ ...
+ Mnemonic data saved to file '89ABCDEF.mmwords'
+
+ $ cat 89ABCDEF.mmwords
+ pleasure tumble spider laughter many stumble secret bother
+ after search float absent path strong curtain savior
+ worst suspend bright touch away dirty measure thorn
+
+Note: a 128- or 192-bit seed will generate a shorter mnemonic of 12 or
+18 words. You may generate a wallet with a these seed lengths
+using the `'-l'` option to `mmgen-walletgen`.
+
+Though some consider 128 bits of entropy to provide adequate security
+for the foreseeable future, you should stick to the default 256-bit
+seed length if you're not planning to use the mnemonic feature.
+
+NOTE: MMGen mnemonics are generated from the Electrum wordlist, only
+using ordinary base conversion instead of Electrum's more complicated
+algorithm.
+
+Generate addresses 1-11 using the mnemonic instead of the wallet:
+
+ $ mmgen-addrgen 89ABCDEF.mmwords 1-11
+ ...
+ Address data saved to file '89ABCDEF[1-11].addrs'
+
+Compare the first ten addresses with those earlier generated by the
+wallet. You'll see they're the same.
+
+Recover a lost wallet using the mnemonic:
+
+ $ mmgen-walletgen 89ABCDEF.mmwords
+ ...
+ Wallet saved to file '89ABCDEF-01234567[256,3].mmdat'
+
+Note that the regenerated wallet has a different Key ID but
+of course the same Seed ID.
+
+Seed files bear the extension '\*.mmseed' and are listed on the command
+line the same way as mnemonic files are.
+
+A seed file for a 256-bit seed looks like this:
+
+ $ cat 8B7392ED.mmseed
+ f4c84b C5ZT wWpT Jsoi wRVw 2dm9 Aftd WLb8 FggQ eC8h Szjd da9L
+
+And for a 128-bit seed:
+
+ $ cat 8E0DFB78.mmseed
+ 0fe02f XnyC NfPH piuW dQ2d nM47 VU
+
+As you can see, the latter is short enough to practically be memorized.
+From the unix command line,
+you can test your memory using the seed's checksum ('0fe02f' in this example)
+as follows:
+
+ $ echo -n XnyCNfPHpiuWdQ2dnM47VU | sha256sum | cut -c 1-6
+ 0fe02f
#### Mnemonics and seeds — additional information:
-> Mnemonic and seed data may be entered at a prompt instead of from a
-> file. Just omit the filename on the command line.
-> Mnemonic and seed data may be printed to standard output instead of a
-> file using the `-S` option of `mmgen-walletchk`.
+With the `'-m'` or `'-s'` option,
+MMGen commands that take mnemonic and seed data may receive the data
+from a prompt instead of a file.
-> Mnemonic and seed files may be output to a directory of your choice
-> using the `-d` option of `mmgen-walletchk`.
+MMGen commands that produce mnemonic and seed data may be forced to
+print it to standard output instead of file with the `'-S'` option.
+This feature was intentionally made optional to safeguard against
+looking-over-the-shoulder, Van Eyck phreaking and other side-channel
+attacks. MMGen commands never print private data to the screen
+unless explicitly asked to.
-> Bear in mind that mnemonic and seed data is unencrypted. If it's
-> compromised, your Bitcoins can easily be stolen. Make sure no one's
-> looking over your shoulder when you print mnemonic or seed data to
-> screen. Securely delete your mnemonic and seed files. In Linux, you
-> can achieve additional security by writing the files to volatile
-> memory in '/dev/shm' instead of disk.
-
-### Vanitygen note:
-> When available, the 'keyconv' utility from the vanitygen package is
-> used to generate addresses as it's much faster than the Python ecdsa
-> library.
+The output file of any MMGen command may be written to a directory of
+your choice using the `'-d'` option. For example, on a Linux system you
+could use `'-d /dev/shm'` to write key and seed data to volatile memory
+instead of disk.
+This also has obvious security benefits, ensuring that no sensitive data
+remains on disk after your computer's been powered down.
### Test suite:
-> To see what tests are available, run the scripts in the 'tests'
-> directory with no arguments. Among others, you might find the
-> following tests to be of interest:
->> Compare 10 addresses generated by 'keyconv' with mmgen's
->> internally-generated ones:
->>> `tests/bitcoin.py keyconv_compare_randloop 10`
+To see what tests are available, run the scripts in the 'tests'
+directory with no arguments. You might find the following tests to be
+of interest:
->> Convert a string to base 58 and back:
->>> `tests/bitcoin.py strtob58 'a string'`
+> Compare 10 addresses generated by 'keyconv' with mmgen's
+> internally-generated ones:
+>> `tests/bitcoin.py keyconv_compare_randloop 10`
->> Convert a hex number to base 58 and back:
->>> `tests/bitcoin.py hextob58 deadbeef`
+> Convert a string to base 58 and back:
+>> `tests/bitcoin.py strtob58 'a string'`
->> Perform 1000 hex -> base58 -> hex conversions, comparing results stringwise:
->>> `tests/bitcoin.py hextob58_pad_randloop 1000`
+> Convert a hex number to base 58 and back:
+>> `tests/bitcoin.py hextob58 deadbeef`
->> Generate a 12-word mnemonic from a random 128-bit seed:
->>> `tests/mnemonic.py random128`
+> Perform 1000 hex -> base58 -> hex conversions, comparing results stringwise:
+>> `tests/bitcoin.py hextob58_pad_randloop 1000`
->> or an 18-word mnemonic from a random 192-bit seed:
->>> `tests/mnemonic.py random192`
+> Generate a 12-word mnemonic from a random 128-bit seed:
+>> `tests/mnemonic.py random128`
->> or a 24-word mnemonic from a random 256-bit seed:
->>> `tests/mnemonic.py random256`
+> or an 18-word mnemonic from a random 192-bit seed:
+>> `tests/mnemonic.py random192`
+
+> or a 24-word mnemonic from a random 256-bit seed:
+>> `tests/mnemonic.py random256`
diff --git a/doc/MMGenLinuxInstall.md b/doc/MMGenLinuxInstall.md
new file mode 100644
index 00000000..67489afb
--- /dev/null
+++ b/doc/MMGenLinuxInstall.md
@@ -0,0 +1,78 @@
+MMGen = Multi-Mode GENerator
+----------------------------
+##### a Bitcoin cold storage solution for the command line
+
+### Download/Install on Debian/Ubuntu Linux:
+
+**Perform the following steps on both your online and offline
+computers:**
+
+Install the pip Python installer:
+
+ $ sudo apt-get install python-pip
+
+Install required Python modules:
+
+ $ sudo pip install ecdsa scrypt pycrypto bitcoin-python
+
+Install MMGen:
+
+ $ git clone https://github.com/mmgen/mmgen.git
+ $ cd mmgen; sudo ./setup.py install
+
+Install vanitygen (optional but recommended):
+
+ $ git clone https://github.com/samr7/vanitygen.git
+ (build and put the 'keyconv' executable in your path)
+
+At this point you can begin trying out MMgen, creating a test wallet
+and generating keys as described in **Using MMGen** below. To be
+able to track addresses and create and sign transactions, however,
+you'll need to have bitcoin daemons installed on your online and
+offline machines.
+
+**Bitcoind installation**
+
+The bitcoin daemon on the **offline machine** is used solely for
+signing transactions and is therefore run without a blockchain.
+The version bundled with the prebuilt Bitcoin-QT is just fine for this
+purpose. It can be obtained here:
+
+ https://bitcoin.org/en/download
+
+After installation, locate the bitcoind executable and start it up:
+
+ $ bitcoind -daemon -maxconnections=0
+
+Note that in the absence of a blockchain the daemon starts very quickly
+and uses practically no CPU once running.
+Thus a low-powered computer such as a netbook can serve quite nicely
+as an offline signing machine.
+
+On the **online machine**, the bitcoin daemon is used for tracking
+addresses and requires a full, updated blockchain. For
+this a more powerful computer is needed, especially when importing
+addresses. Plenty of free disk space is also required to accomodate
+the rapidly-growing blockchain (20GB in size at the time of writing).
+
+The standard bitcoin daemon at present lacks the watch-only address
+support we need, so we must get and compile the special watch-only
+enabled version created by Bitcoin core developer Pieter Wuille, aka
+sipa. If you have the necessary dependencies installed, this process
+is surprisingly painless.
+
+ $ curl -O https://codeload.github.com/sipa/bitcoin/zip/watchonly
+ $ unzip watchonly
+ $ cd bitcoin-watchonly
+ $ ./autogen.sh
+ $ ./configure
+ $ make -j4
+ (You may have to install the libboost-all-dev package for the build to succeed)
+
+With your online machine connected to the Internet, start the freshly
+compiled daemon and let it synchronize the blockchain, **taking care
+to move any existing wallet.dat out of harm's way** beforehand.
+The daemon will create a new wallet upon startup, which you'll use
+as your **tracking wallet**.
+
+Congratulations! Your MMGen installation is now complete.
diff --git a/doc/MMGenWindowsInstall.md b/doc/MMGenWindowsInstall.md
new file mode 100644
index 00000000..cd1cb8aa
--- /dev/null
+++ b/doc/MMGenWindowsInstall.md
@@ -0,0 +1,22 @@
+MMGen = Multi-Mode GENerator
+----------------------------
+##### a Bitcoin cold storage solution for the command line
+
+### Download/Install on Microsoft Windows
+
+ Compiling the watch-only bitcoind and the python scrypt module are the
+ main challenges for Windows installation.
+ To compile bitcoind, MinGW must first be installed
+ and the Berkeley DB and Boost libraries built from source.
+ The Python interpeter is required for building the python scrypt
+ module and installing the other python modules.
+
+ The build process involves some editing of source code and configuration
+ files, so it's useful to have a good text editor installed on the system.
+
+ MMGen's remaining components either require no compilation (the ecdsa and
+ bitcoin-python modules and MMGen itself) or can be found online in
+ precompiled form (the Python interpeter, pycrypto Python module and
+ vanitygen)
+
+##### (to be continued...)
diff --git a/mmgen-addrgen b/mmgen-addrgen
index 050baea1..33c55fb8 100755
--- a/mmgen-addrgen
+++ b/mmgen-addrgen
@@ -25,7 +25,7 @@ mmgen-addrgen: Generate a list or range of addresses from a mmgen
import sys
from mmgen.Opts import *
-from mmgen.config import *
+import mmgen.config as g
from mmgen.license import *
from mmgen.utils import *
from mmgen.addr import *
@@ -44,7 +44,7 @@ else: extra_help_data = ("","","")
help_data = {
'prog_name': sys.argv[0].split("/")[-1],
'desc': """Generate a list or range of {} from a {} wallet,
- mnemonic, seed or password""".format(gen_what,proj_name),
+ mnemonic, seed or password""".format(gen_what,g.proj_name),
'usage':"[opts] [infile]
",
'options': """
-h, --help Print this help message{}
@@ -97,13 +97,13 @@ addresses, the same 'l' and 'p' parameters to '--from-brain' must be used
in all future invocations with that passphrase
""".format(
extra_help_data[0],
- ", ".join([str(i) for i in seed_lens]),
- seed_len,
- hash_preset,
+ ", ".join([str(i) for i in g.seed_lens]),
+ g.seed_len,
+ g.hash_preset,
extra_help_data[1],
extra_help_data[2],
W=gen_what,
- S=seed_ext
+ S=g.seed_ext
)
}
@@ -126,7 +126,7 @@ opts['gen_what'] = gen_what
check_opts(opts,long_opts)
-if debug: show_opts_and_cmd_args(opts,cmd_args)
+if g.debug: show_opts_and_cmd_args(opts,cmd_args)
if len(cmd_args) == 1 and (
'from_mnemonic' in opts or
diff --git a/mmgen-addrimport b/mmgen-addrimport
index a1cfbf47..68f17012 100755
--- a/mmgen-addrimport
+++ b/mmgen-addrimport
@@ -57,7 +57,8 @@ else:
seed_id,addr_data = "",[]
if 'addrlist' in opts:
- l = get_lines_from_file(opts['addrlist'],"non-mmgen addresses")
+ l = get_lines_from_file(
+ opts['addrlist'],"non-mmgen addresses",remove_comments=True)
addr_data += [(None,i) for i in l]
msg_r("Validating addresses...")
@@ -68,8 +69,8 @@ for i in addr_data:
msg("OK")
-import mmgen.config
-mmgen.config.http_timeout = 3600
+import mmgen.config as g
+g.http_timeout = 3600
c = connect_to_bitcoind()
@@ -82,17 +83,28 @@ Importing addresses can take a long time (>30 min.) on a low-powered computer.
import threading
import time
+err_flag = False
+
+def import_address(addr,label):
+ try:
+ c.importaddress(addr,label)
+ except:
+ global err_flag
+ err_flag = True
+
+
w1 = len(str(len(addr_data))) * 2 + 2
w2 = len(str(max([i[0] for i in addr_data if i[0]]))) + 12
-msg_fmt = "\rImporting %-" + str(w1) + "s %-34s %-" + str(w2) + "s %s"
+msg_fmt = "\r%s %-" + str(w1) + "s %-34s %-" + str(w2) + "s"
+msg("Importing addresses")
for n,i in enumerate(addr_data):
if i[0]:
comment = " " + i[2] if len(i) == 3 else ""
label = "%s:%s%s" % (seed_id,i[0],comment)
else: label = "non-mmgen"
- t = threading.Thread(target=c.importaddress, args = (i[1],label))
+ t = threading.Thread(target=import_address, args = (i[1],label))
t.daemon = True
t.start()
@@ -102,11 +114,15 @@ for n,i in enumerate(addr_data):
if t.is_alive():
elapsed = int(time.time() - start)
msg_r(msg_fmt % (
+ secs_to_hms(elapsed),
("%s/%s:" % (n+1,len(addr_data))),
- i[1], "(" + label + ")", secs_to_hms(elapsed))
+ i[1], "(" + label + ")"
+ )
)
+ time.sleep(1)
else:
- msg("")
+ if err_flag:
+ msg("\nImport failed")
+ sys.exit(2)
+ msg("\nOK")
break
-
- time.sleep(1)
diff --git a/mmgen-passchg b/mmgen-passchg
index f16e4ac9..7d9ae728 100755
--- a/mmgen-passchg
+++ b/mmgen-passchg
@@ -23,11 +23,11 @@ mmgen-passchg: Change a mmgen deterministic wallet's passphrase, label or
import sys
from mmgen.Opts import *
from mmgen.utils import *
-from mmgen.config import *
+import mmgen.config as g
help_data = {
'desc': """Change the passphrase, hash preset or label of a {}
- deterministic wallet""".format(proj_name),
+ deterministic wallet""".format(g.proj_name),
'usage': "[opts] [filename]",
'options': """
-h, --help Print this help message
@@ -43,7 +43,7 @@ help_data = {
NOTE: The key ID will change if either the passphrase or hash preset
are changed
-""".format(hash_preset)
+""".format(g.hash_preset)
}
short_opts = "hd:HkL:p:P:v"
@@ -112,7 +112,7 @@ if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
from hashlib import sha256
from Crypto import Random
- salt = sha256(salt + Random.new().read(128)).digest()[:salt_len]
+ salt = sha256(salt + Random.new().read(128)).digest()[:g.salt_len]
key = make_key(passwd, salt, opts['hash_preset'])
new_key_id = make_chksum_8(key)
msg("Key ID changed: %s -> %s" % (key_id,new_key_id))
diff --git a/mmgen-txcreate b/mmgen-txcreate
index 066dbba6..0166642b 100755
--- a/mmgen-txcreate
+++ b/mmgen-txcreate
@@ -24,7 +24,7 @@ import sys
from mmgen.Opts import *
from mmgen.license import *
-from mmgen.config import *
+import mmgen.config as g
from mmgen.tx import *
from mmgen.utils import msg, msg_r, user_confirm
from decimal import Decimal
@@ -33,8 +33,8 @@ prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
- 'desc': "Create a BTC transaction, sending to specified addresses",
- 'usage': "[opts] [,...] ",
+ 'desc': "Create a BTC transaction with outputs to specified addresses",
+ 'usage': "[opts] ... [change addr] [tx fee] [addr file] ...",
'options': """
-h, --help Print this help message
-d, --outdir d Specify an alternate directory 'd' for output
@@ -43,42 +43,85 @@ help_data = {
-q, --quiet Suppress warnings; overwrite files without
prompting
-Outputs to spend are chosen by the user via a menu.
+-f, --tx-fee f Transaction fee (default: %s BTC)
-Ages of transactions are approximate based on an average block discovery
+Transaction inputs are chosen from a list of the user's unpent outputs
+via an interactive menu.
+
+Ages of transactions are approximate based on an average block creation
interval of %s minutes.
-""" % mins_per_block
+
+Addresses on the command line can be Bitcoin addresses or MMGen
+addresses in the form :
+""" % (Decimal(g.tx_fee),g.mins_per_block)
}
-short_opts = "hd:eiq"
-long_opts = "help","outdir=","echo_passphrase","info","quiet"
+short_opts = "ha:d:eiqf:"
+long_opts = "help","addr_file","outdir=","echo_passphrase","info","quiet",\
+ "tx_fee="
opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
check_opts(opts,long_opts)
-if debug: show_opts_and_cmd_args(opts,cmd_args)
-
-if len(cmd_args) == 3:
- rcpt_arg,tx_fee,change_addr = cmd_args
- check_address(change_addr)
-elif len(cmd_args) == 2:
- rcpt_arg,tx_fee = cmd_args
- change_addr = ""
-elif len(cmd_args) == 0 and 'info' in opts:
- pass
-else: usage(help_data)
+if g.debug: show_opts_and_cmd_args(opts,cmd_args)
if not 'info' in opts:
- tx_out = make_tx_out(rcpt_arg)
+
+ outputs,addr_files,change_addr = [],[],""
+
+ for a in cmd_args:
+ if a.split(".")[-1] == g.addrfile_ext:
+ check_infile(a)
+ addr_files.append(a)
+ elif "," in a:
+ outputs.append(a)
+ else:
+ if change_addr:
+ msg("More than one change address specified: %s, %s" %
+ (change_addr, a))
+ sys.exit(2)
+ change_addr = a
+
+ if not outputs:
+ msg("At least one output must be specified on the command line")
+ sys.exit(2)
+
+ addr_data = [parse_addrs_file(f) for f in addr_files]
+
+ tx_fee = opts['tx_fee'] if 'tx_fee' in opts else g.tx_fee
+
+ try:
+ tx_fee = Decimal(tx_fee)
+ except:
+ msg("Invalid transaction fee format: %s" % tx_fee)
+ sys.exit(2)
+
+ if tx_fee > g.max_tx_fee:
+ msg("Transaction fee too large: %s > %s" % (tx_fee,g.max_tx_fee))
+ sys.exit(2)
+
+ if change_addr:
+ if ":" in change_addr:
+ change_addr = mmgen_addr_to_btc_addr(change_addr,addr_data)
+ else:
+ check_address(change_addr)
+
+
+ tx_out = make_tx_out(outputs,addr_data)
+
for i in tx_out.keys(): check_address(i)
for i in tx_out.values(): check_btc_amt(i)
- tx_fee = check_btc_amt(tx_fee)
+
+ tx_fee = check_btc_amt(tx_fee)
+
+if g.debug: show_opts_and_cmd_args(opts,cmd_args)
# Begin execution
c = connect_to_bitcoind()
-if not 'quiet' in opts and not 'info' in opts: do_license_msg(immed=True)
+if not 'quiet' in opts and not 'info' in opts:
+ do_license_msg(immed=True)
# Begin test
# import mmgen.rpc
@@ -140,6 +183,9 @@ if change > 0 and not change_addr:
tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
for i in tx_out.keys(): tx_out[i] = float(tx_out[i])
if change: tx_out[change_addr] = float(change)
+if g.debug:
+ print "tx_in:", repr(tx_in)
+ print "tx_out:", repr(tx_out)
tx_hex = c.createrawtransaction(tx_in,tx_out)
msg("Transaction successfully created")
diff --git a/mmgen-txsend b/mmgen-txsend
index 7b9f534a..93592980 100755
--- a/mmgen-txsend
+++ b/mmgen-txsend
@@ -23,7 +23,7 @@ import sys
from mmgen.Opts import *
from mmgen.license import *
-from mmgen.config import *
+import mmgen.config as g
from mmgen.tx import *
from mmgen.utils import msg,check_infile,get_lines_from_file,confirm_or_exit
diff --git a/mmgen-txsign b/mmgen-txsign
index 2a540e78..abe3fb21 100755
--- a/mmgen-txsign
+++ b/mmgen-txsign
@@ -23,7 +23,7 @@ import sys
from mmgen.Opts import *
from mmgen.license import *
-from mmgen.config import *
+import mmgen.config as g
from mmgen.tx import *
from mmgen.utils import msg
@@ -69,7 +69,7 @@ Seed data supplied in files must have the following extensions:
seed: '.{}'
mnemonic: '.{}'
brainwallet: '.{}'
-""".format(seed_ext,wallet_ext,seed_ext,mn_ext,brain_ext)
+""".format(g.seed_ext,g.wallet_ext,g.seed_ext,g.mn_ext,g.brain_ext)
}
short_opts = "hd:eiIk:P:qb:msw"
diff --git a/mmgen-walletchk b/mmgen-walletchk
index 2565a5a2..01688adb 100755
--- a/mmgen-walletchk
+++ b/mmgen-walletchk
@@ -29,7 +29,7 @@ help_data = {
'prog_name': sys.argv[0].split("/")[-1],
'desc': """Check integrity of a %s deterministic wallet, display
its information and export seed and mnemonic data."""\
- % proj_name,
+ % g.proj_name,
'usage': "[opts] [filename]",
'options': """
-h, --help Print this help message
@@ -68,8 +68,8 @@ if 'export_mnemonic' in opts:
wl = get_default_wordlist()
from mmgen.mnemonic import get_mnemonic_from_seed
- p = True if debug else False
- mn = get_mnemonic_from_seed(seed, wl, default_wl, print_info=p)
+ p = True if g.debug else False
+ mn = get_mnemonic_from_seed(seed, wl, g.default_wl, print_info=p)
write_mnemonic(mn, seed, opts)
diff --git a/mmgen-walletgen b/mmgen-walletgen
index fb6154c9..99a65672 100755
--- a/mmgen-walletgen
+++ b/mmgen-walletgen
@@ -24,14 +24,14 @@ from hashlib import sha256
from mmgen.Opts import *
from mmgen.license import *
-from mmgen.config import *
+import mmgen.config as g
from mmgen.walletgen import *
from mmgen.utils import *
prog_name = sys.argv[0].split("/")[-1]
help_data = {
'prog_name': prog_name,
- 'desc': "Generate a {} deterministic wallet".format(proj_name),
+ 'desc': "Generate a {} deterministic wallet".format(g.proj_name),
'usage': "[opts] [infile]",
'options': """
-h, --help Print this help message
@@ -83,12 +83,12 @@ For a brainwallet passphrase to always generate the same keys and
addresses, the same 'l' and 'p' parameters to '--from-brain' must be used
in all future invocations with that passphrase.
""".format(
- ",".join([str(i) for i in seed_lens]),
- seed_len,
- hash_preset,
- usr_randlen,
+ ",".join([str(i) for i in g.seed_lens]),
+ g.seed_len,
+ g.hash_preset,
+ g.usr_randlen,
prog_name,
- S=seed_ext,
+ S=g.seed_ext,
)
}
@@ -103,7 +103,7 @@ if 'show_hash_presets' in opts: show_hash_presets()
check_opts(opts,long_opts)
-if debug: show_opts_and_cmd_args(opts,cmd_args)
+if g.debug: show_opts_and_cmd_args(opts,cmd_args)
if len(cmd_args) == 1:
infile = cmd_args[0]
@@ -131,12 +131,12 @@ except:
msg("OK")
-if debug: display_os_random_data(os_rand_data)
+if g.debug: display_os_random_data(os_rand_data)
usr_keys,key_timings = get_random_data_from_user(opts)
msg("")
-if debug: display_user_random_data(usr_keys,key_timings)
+if g.debug: display_user_random_data(usr_keys,key_timings)
usr_rand_data = sha256(usr_keys).digest() + \
sha256("".join(key_timings)).digest()
@@ -150,7 +150,7 @@ else:
seed = sha256(seed).digest()[:opts['seed_len']/8]
salt = os_rand_data[1] + usr_rand_data
-salt = sha256(salt).digest()[:salt_len]
+salt = sha256(salt).digest()[:g.salt_len]
if not 'quiet' in opts:
msg("""
@@ -160,7 +160,7 @@ no strength checking of passphrases is performed. For an empty passphrase,
just hit ENTER twice.
""" % opts['hash_preset'])
-passwd = get_new_passphrase("{} wallet passphrase".format(proj_name), opts)
+passwd = get_new_passphrase("{} wallet passphrase".format(g.proj_name), opts)
key = make_key(passwd, salt, opts['hash_preset'])
diff --git a/mmgen/Opts.py b/mmgen/Opts.py
index 6c44d945..17f5b0a5 100755
--- a/mmgen/Opts.py
+++ b/mmgen/Opts.py
@@ -17,7 +17,7 @@
# along with this program. If not, see .
import sys, getopt
-from mmgen.config import *
+import mmgen.config as g
from mmgen.utils import msg
def usage(hd):
@@ -37,7 +37,7 @@ def process_opts(argv,help_data,short_opts,long_opts):
progname = argv[0].split("/")[-1]
- if debug:
+ if g.debug:
print "Short opts: %s" % repr(short_opts)
print "Long opts: %s" % repr(long_opts)
@@ -65,7 +65,7 @@ def process_opts(argv,help_data,short_opts,long_opts):
opts[long_opts[short_opts_l.index(opt[1:]+":")][:-1].replace("-","_")] = arg
else: assert False, "Invalid option"
- if debug: print "User-selected options: %s" % repr(opts)
+ if g.debug: print "User-selected options: %s" % repr(opts)
return opts,args
@@ -73,7 +73,7 @@ def process_opts(argv,help_data,short_opts,long_opts):
def check_opts(opts,long_opts):
# These must be set to the default values in mmgen.config:
- for i in cl_override_vars:
+ for i in g.cl_override_vars:
if i+"=" in long_opts:
set_if_unset_and_typeconvert(opts,i)
@@ -110,14 +110,13 @@ def check_opts(opts,long_opts):
label = val.strip()
opts[opt] = label
- if len(label) > 32:
- msg("Label must be 32 characters or less")
+ if len(label) > g.max_wallet_label_len:
+ msg("Label must be %s characters or less" %
+ g.max_wallet_label_len)
sys.exit(1)
- from string import ascii_letters, digits
- label_chrs = list(ascii_letters + digits) + [".", "_", " "]
for ch in list(label):
- if ch not in label_chrs:
+ if ch not in g.wallet_label_symbols:
msg("'%s': illegal character in label" % ch)
sys.exit(1)
elif opt == 'from_brain':
@@ -133,32 +132,32 @@ def check_opts(opts,long_opts):
msg("'%s': invalid 'l' %s (not an integer)" % (l,what))
sys.exit(1)
- if int(l) not in seed_lens:
+ if int(l) not in g.seed_lens:
msg("'%s': invalid 'l' %s. Options: %s" %
- (l, what, ", ".join([str(i) for i in seed_lens])))
+ (l, what, ", ".join([str(i) for i in g.seed_lens])))
sys.exit(1)
- if p not in hash_presets:
- hps = ", ".join([i for i in sorted(hash_presets.keys())])
+ if p not in g.hash_presets:
+ hps = ", ".join([i for i in sorted(g.hash_presets.keys())])
msg("'%s': invalid 'p' %s. Options: %s" % (p, what, hps))
sys.exit(1)
elif opt == 'seed_len':
- if val not in seed_lens:
+ if val not in g.seed_lens:
msg("'%s': invalid %s. Options: %s"
- % (val,what,", ".join([str(i) for i in seed_lens])))
+ % (val,what,", ".join([str(i) for i in g.seed_lens])))
sys.exit(2)
elif opt == 'hash_preset':
- if val not in hash_presets:
+ if val not in g.hash_presets:
msg("'%s': invalid %s. Options: %s"
- % (val,what,", ".join(sorted(hash_presets.keys()))))
+ % (val,what,", ".join(sorted(g.hash_presets.keys()))))
sys.exit(2)
elif opt == 'usr_randlen':
- if val > max_randlen or val < min_randlen:
+ if val > g.max_randlen or val < g.min_randlen:
msg("'%s': invalid %s (must be >= %s and <= %s)"
- % (val,what,min_randlen,max_randlen))
+ % (val,what,g.min_randlen,g.max_randlen))
sys.exit(2)
else:
- if debug: print "check_opts(): No test for opt '%s'" % opt
+ if g.debug: print "check_opts(): No test for opt '%s'" % opt
def show_opts_and_cmd_args(opts,cmd_args):
@@ -168,14 +167,16 @@ def show_opts_and_cmd_args(opts,cmd_args):
def set_if_unset_and_typeconvert(opts,opt):
- if opt in cl_override_vars:
+ if opt in g.cl_override_vars:
if opt not in opts:
# Set to similarly named default value in mmgen.config
- opts[opt] = eval(opt)
+ opts[opt] = eval("g."+opt)
else:
- vtype = type(eval(opt))
- if vtype == int: f,t = int,"an integer"
- elif vtype == str: f,t = str,"a string"
+ vtype = type(eval("g."+opt))
+ if g.debug: print "Opt: %s, Type: %s" % (opt,vtype)
+ if vtype == int: f,t = int,"an integer"
+ elif vtype == str: f,t = str,"a string"
+ elif vtype == float: f,t = float,"a float"
try:
opts[opt] = f(opts[opt])
diff --git a/mmgen/addr.py b/mmgen/addr.py
index 3085cd4c..a5df8cb7 100755
--- a/mmgen/addr.py
+++ b/mmgen/addr.py
@@ -24,25 +24,23 @@ from hashlib import sha256, sha512
from binascii import hexlify, unhexlify
from mmgen.bitcoin import numtowif
-from mmgen.config import *
+import mmgen.config as g
def test_for_keyconv():
"""
Test for the presence of 'keyconv' utility on system
"""
- keyconv_exec = "keyconv"
-
from subprocess import Popen, PIPE
try:
- p = Popen([keyconv_exec, '-h'], stdout=PIPE, stderr=PIPE)
+ p = Popen([g.keyconv_exec, '-h'], stdout=PIPE, stderr=PIPE)
except:
sys.stderr.write("""
Executable '%s' unavailable. Falling back on (slow) internal ECDSA library.
Please install '%s' from the %s package on your system for much
faster address generation.
-""" % (keyconv_exec, keyconv_exec, "vanitygen"))
+""" % (g.keyconv_exec, g.keyconv_exec, "vanitygen"))
return False
else:
return True
@@ -157,9 +155,8 @@ def format_addr_data(addrlist, seed_chksum, opts):
# address, and it will be appended to the bitcoind wallet label upon import.
# The label may contain ASCII letters, numerals, and the symbols
# '{}' and '{}'.
-""".format(proj_name.capitalize(),max_addr_label_len,
- "', '".join(addr_label_symbols[0:-1]),
- addr_label_symbols[-1]).strip()
+""".format(g.proj_name.capitalize(),g.max_addr_label_len,
+ "', '".join(g.addr_label_punc[0:-1]), g.addr_label_punc[-1]).strip()
data = []
if not 'stdout' in opts: data.append(header + "\n")
data.append("%s {" % seed_chksum.upper())
@@ -208,8 +205,8 @@ def fmt_addr_list(addr_list):
def write_addr_data_to_file(seed, data, addr_list, opts):
- if 'print_addresses_only' in opts: ext = "addrs"
- elif 'no_addresses' in opts: ext = "keys"
+ if 'print_addresses_only' in opts: ext = g.addrfile_ext
+ elif 'no_addresses' in opts: ext = g.keyfile_ext
else: ext = "akeys"
if 'b16' in opts: ext = ext.replace("keys","xkeys")
diff --git a/mmgen/config.py b/mmgen/config.py
index 5a93e99e..ee6bfa9f 100755
--- a/mmgen/config.py
+++ b/mmgen/config.py
@@ -18,6 +18,11 @@
"""
config.py: Constants and configuration options for the mmgen suite
"""
+
+from decimal import Decimal
+tx_fee = Decimal("0.001")
+max_tx_fee = Decimal("0.1")
+
proj_name = "mmgen"
wallet_ext = "mmdat"
@@ -25,7 +30,10 @@ seed_ext = "mmseed"
mn_ext = "mmwords"
brain_ext = "mmbrain"
-seed_exts = wallet_ext, seed_ext, mn_ext, brain_ext
+seedfile_exts = wallet_ext, seed_ext, mn_ext, brain_ext
+
+addrfile_ext = "addrs"
+keyfile_ext = "keys"
default_wl = "electrum"
#default_wl = "tirosh"
@@ -39,6 +47,8 @@ mnemonic_lens = [i / 32 * 3 for i in seed_lens]
http_timeout = 30
+keyconv_exec = "keyconv"
+
from os import getenv
debug = True if getenv("MMGEN_DEBUG") else False
@@ -60,5 +70,13 @@ hash_presets = {
'5': [16, 8, 16],
'6': [17, 8, 20],
}
-addr_label_symbols = ".","_",",","-"," "
+
+from string import ascii_letters, digits
+
+addr_label_punc = ".","_",",","-"," "
+addr_label_symbols = tuple(ascii_letters + digits) + addr_label_punc
max_addr_label_len = 16
+
+wallet_label_punc = ".", "_", " "
+wallet_label_symbols = tuple(ascii_letters + digits) + wallet_label_punc
+max_wallet_label_len = 32
diff --git a/mmgen/license.py b/mmgen/license.py
index 2d2fab2a..cea8e8a2 100755
--- a/mmgen/license.py
+++ b/mmgen/license.py
@@ -20,7 +20,6 @@ license.py: Show the license
"""
import sys
-from mmgen.config import proj_name
from mmgen.utils import msg, msg_r, get_char
gpl = {
diff --git a/mmgen/mnemonic.py b/mmgen/mnemonic.py
index 34a5c8e5..b9e06cf5 100755
--- a/mmgen/mnemonic.py
+++ b/mmgen/mnemonic.py
@@ -32,7 +32,7 @@ def mn_len(hexnum): return len(hexnum) * 3 / 8
import sys
from mmgen.utils import msg,make_chksum_8
-from mmgen.config import *
+import mmgen.config as g
# These universal base-conversion routines work for any base
@@ -47,9 +47,9 @@ def hextobaseN(base,hexnum,wordlist,mn_len):
def get_seed_from_mnemonic(mn,wl):
- if len(mn) not in mnemonic_lens:
+ if len(mn) not in g.mnemonic_lens:
msg("Bad mnemonic (%i words). Allowed numbers of words: %s" %
- (len(mn),", ".join([str(i) for i in mnemonic_lens])))
+ (len(mn),", ".join([str(i) for i in g.mnemonic_lens])))
return False
for n,w in enumerate(mn,1):
diff --git a/mmgen/rpc/connection.py b/mmgen/rpc/connection.py
index 6623ddda..0fe6fc82 100755
--- a/mmgen/rpc/connection.py
+++ b/mmgen/rpc/connection.py
@@ -62,14 +62,14 @@ class BitcoinConnection(object):
"""
"""
try:
+# return self.proxy.badmethod(address,label) # DEBUG
return self.proxy.importaddress(address,label)
except JSONRPCException as e:
if e.error['message'] == "Method not found":
- from mmgen.utils import msg
- msg("""
-ERROR: 'importaddress' method not found. Is your bitcoind enabled for
-watch-only addresses?""")
- else: raise _wrap_exception(e.error)
+ from mmgen.utils import msg_r
+ msg_r("""
+ERROR: 'importaddress' method not found. Is your bitcoind enabled for watch-only addresses?""")
+ raise _wrap_exception(e.error)
# sendrawtransaction [allowhighfees=false]
def sendrawtransaction(self,tx):
diff --git a/mmgen/rpc/proxy.py b/mmgen/rpc/proxy.py
index a22decfe..6e13fdda 100755
--- a/mmgen/rpc/proxy.py
+++ b/mmgen/rpc/proxy.py
@@ -58,7 +58,7 @@ class JSONRPCException(Exception):
self.error = rpcError
-import mmgen.config
+import mmgen.config as g
class AuthServiceProxy(object):
def __init__(self, serviceURL, serviceName = None):
@@ -75,7 +75,7 @@ class AuthServiceProxy(object):
authpair = authpair.encode('utf8')
self.__authhdr = "Basic ".encode('utf8') + base64.b64encode(authpair)
- http_timeout = mmgen.config.http_timeout
+ http_timeout = g.http_timeout
if self.__url.scheme == 'https':
self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
diff --git a/mmgen/tx.py b/mmgen/tx.py
index c69c687b..18f5d3f1 100755
--- a/mmgen/tx.py
+++ b/mmgen/tx.py
@@ -23,7 +23,7 @@ from binascii import unhexlify
from mmgen.utils import *
import sys, os
from decimal import Decimal
-from mmgen.config import *
+import mmgen.config as g
txmsg = {
'not_enough_btc': "Not enough BTC in the inputs for this transaction (%s BTC)",
@@ -37,21 +37,22 @@ specified recipient address.
NOTE: This transaction uses a mixture of both mmgen and non-mmgen inputs,
which makes the signing process more complicated. When signing the
transaction, keys for the non-mmgen inputs must be supplied in a separate
-file using the '-k' option of mmgen-txsign.
-
-Alternatively, you may import the mmgen keys into the wallet.dat of your
-offline bitcoind, first generating the required keys with mmgen-keygen and
-then running mmgen-txsign with the '-f' option to force the use of
-wallet.dat as the key source.
+file using the '-k' option to mmgen-txsign.
Selected mmgen inputs: %s"""
}
+# Deleted text:
+# Alternatively, you may import the mmgen keys into the wallet.dat of your
+# offline bitcoind, first generating the required keys with mmgen-keygen and
+# then running mmgen-txsign with the '-f' option to force the use of
+# wallet.dat as the key source.
+
def connect_to_bitcoind():
host,port,user,passwd = "localhost",8332,"rpcuser","rpcpassword"
- cfg = get_cfg_options((user,passwd))
+ cfg = get_bitcoind_cfg_options((user,passwd))
import mmgen.rpc.connection
f = mmgen.rpc.connection.BitcoinConnection
@@ -72,7 +73,7 @@ def trim_exponent(n):
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
-def check_address(rcpt_address):
+def check_address(rcpt_address):
from mmgen.bitcoin import verify_addr
if not verify_addr(rcpt_address):
sys.exit(3)
@@ -87,6 +88,9 @@ def check_btc_amt(send_amt):
msg("%s: Invalid amount" % send_amt)
sys.exit(3)
+ if g.debug:
+ print "Decimal(amt): %s\nAs tuple: %s" % (send_amt,repr(retval.as_tuple()))
+
if retval.as_tuple()[-1] < -8:
msg("%s: Too many decimal places in amount" % send_amt)
sys.exit(3)
@@ -94,16 +98,18 @@ def check_btc_amt(send_amt):
return trim_exponent(retval)
-def get_cfg_options(cfg_keys):
+def get_bitcoind_cfg_options(cfg_keys):
if "HOME" in os.environ:
- cfg_file = "%s/%s" % (os.environ["HOME"], ".bitcoin/bitcoin.conf")
+ data_dir = ".bitcoin"
+ cfg_file = "%s/%s/%s" % (os.environ["HOME"], data_dir, "bitcoin.conf")
elif "HOMEPATH" in os.environ:
# Windows:
- cfg_file = "%s%s" % (os.environ["HOMEPATH"],
- r"\Application Data\Bitcoin\bitcoin.conf")
+ data_dir = r"Application Data\Bitcoin"
+ cfg_file = "%s\%s\%s" % (os.environ["HOMEPATH"],data_dir,"bitcoin.conf")
else:
- msg("Unable to find bitcoin configuration file")
+ msg("Neither $HOME nor %HOMEPATH% is set")
+ msg("Don't know where to look for 'bitcoin.conf'")
sys.exit(3)
try:
@@ -202,7 +208,7 @@ def sort_and_view(unspent):
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 * mins_per_block / (60*24))
+ i.days = int(i.confirmations * g.mins_per_block / (60*24))
for n,i in enumerate(out):
if i.skip == "d":
@@ -321,7 +327,7 @@ def view_tx_data(c,inputs_data,tx_hex,metadata=[],pager=False):
for n,i in enumerate(td['vin']):
for j in inputs_data:
if j['txid'] == i['txid'] and j['vout'] == i['vout']:
- days = int(j['confirmations'] * mins_per_block / (60*24))
+ days = int(j['confirmations'] * g.mins_per_block / (60*24))
total_in += j['amount']
out += (" " + """
%-2s tx,vout: %s,%s
@@ -391,7 +397,7 @@ def parse_tx_data(tx_data,infile):
def select_outputs(unspent,prompt):
while True:
- reply = my_raw_input(prompt).strip()
+ reply = my_raw_input(prompt,allowed_chars="0123456789 -").strip()
if not reply: continue
@@ -407,46 +413,80 @@ def select_outputs(unspent,prompt):
return selected
+def mmgen_addr_to_btc_addr(m,addr_data):
-def make_tx_out(rcpt_arg):
+ ID,num = m.split(":")
+ from binascii import unhexlify
+ try: unhexlify(ID)
+ except: pass
+ else:
+ try: num = int(num)
+ except: pass
+ else:
+ if not addr_data:
+ msg("Address data must be supplied for MMgen address '%s'" % m)
+ sys.exit(2)
+ for i in addr_data:
+ if ID == i[0]:
+ for j in i[1]:
+ if j[0] == num:
+ return j[1]
+ msg("MMgen address '%s' not found in supplied address data" % m)
+ sys.exit(2)
+
+ msg("Invalid format: %s" % m)
+ sys.exit(3)
+
+
+
+def make_tx_out(tx_arg,addr_data):
+
+ tx = {}
+ for i in tx_arg:
+ addr,amt = i.split(",")
+
+ if ":" in addr:
+ addr = mmgen_addr_to_btc_addr(addr,addr_data)
+ else:
+ check_address(addr)
+
+ try: tx[addr] = amt
+ except:
+ msg("Invalid format: %s: %s" % (addr,amt))
+ sys.exit(3)
+
+ if g.debug:
+ print "TX (cl): ", repr(tx_arg)
+ print "TX (proc): ", repr(tx)
import decimal
try:
- tx_out = dict([(i.split(":")[0],i.split(":")[1])
- for i in rcpt_arg.split(",")])
- except:
- msg("Invalid format: %s" % rcpt_arg)
- sys.exit(3)
-
- try:
- for i in tx_out.keys():
- tx_out[i] = trim_exponent(Decimal(tx_out[i]))
+ for i in tx.keys():
+ tx[i] = trim_exponent(Decimal(tx[i]))
except decimal.InvalidOperation:
- msg("Decimal conversion error in suboption '%s:%s'" % (i,tx_out[i]))
+ msg("Decimal conversion error in suboption '%s:%s'" % (i,tx[i]))
sys.exit(3)
- return tx_out
+ return tx
+
def check_addr_comment(label):
- if len(label) > max_addr_label_len:
+ if len(label) > g.max_addr_label_len:
msg("'%s': overlong label (length must be <=%s)" %
- (label,max_addr_label_len))
+ (label,g.max_addr_label_len))
sys.exit(3)
- from string import ascii_letters, digits
- chrs = tuple(ascii_letters + digits) + addr_label_symbols
for ch in list(label):
- if ch not in chrs:
+ if ch not in g.addr_label_symbols:
msg("'%s': illegal character in label '%s'" % (ch,label))
msg("Permitted characters: A-Za-z0-9, plus '%s'" %
- "', '".join(addr_label_symbols))
+ "', '".join(g.addr_label_punc))
sys.exit(3)
def parse_addrs_file(f):
- lines = get_lines_from_file(f,"address data")
- lines = remove_blanks_comments(lines)
+ lines = get_lines_from_file(f,"address data",remove_comments=True)
try:
seed_id,obrace = lines[0].split()
@@ -496,7 +536,7 @@ def sign_transaction(c,tx_hex,sig_data,keys=None):
if keys:
msg("%s keys total" % len(keys))
- if debug: print "Keys:\n %s" % "\n ".join(keys)
+ if g.debug: print "Keys:\n %s" % "\n ".join(keys)
from mmgen.rpc import exceptions
@@ -591,3 +631,8 @@ for the following non-mmgen address%s: %s""" %
("" if len(other_addrs) == 1 else "es",
" ".join([i['address'] for i in other_addrs])
))
+
+def get_addr_data(cmd_args):
+ for f in cmd_args:
+ data = parse_addrs_file(f)
+ print repr(data); sys.exit() # DEBUG
diff --git a/mmgen/utils.py b/mmgen/utils.py
index 55079a94..bf030b69 100755
--- a/mmgen/utils.py
+++ b/mmgen/utils.py
@@ -20,7 +20,7 @@ utils.py: Shared routines for the mmgen suite
"""
import sys
-from mmgen.config import *
+import mmgen.config as g
from binascii import hexlify,unhexlify
from mmgen.bitcoin import b58decode_pad
@@ -28,6 +28,25 @@ def msg(s): sys.stderr.write(s + "\n")
def msg_r(s): sys.stderr.write(s)
def bail(): sys.exit(9)
+def kb_hold_protect_unix():
+
+ fd = sys.stdin.fileno()
+ old = termios.tcgetattr(fd)
+ tty.setcbreak(fd)
+
+ timeout = float(0.3)
+
+ try:
+ while True:
+ key = select([sys.stdin], [], [], timeout)[0]
+ if key: sys.stdin.read(1)
+ else: break
+ except:
+ print "\nUser interrupt"
+ sys.exit(1)
+ finally:
+ termios.tcsetattr(fd, termios.TCSADRAIN, old)
+
def get_keypress_unix(prompt="",immed_chars=""):
@@ -56,6 +75,24 @@ def get_keypress_unix(prompt="",immed_chars=""):
termios.tcsetattr(fd, termios.TCSADRAIN, old)
+def kb_hold_protect_mswin():
+
+ timeout = float(0.5)
+
+ try:
+ while True:
+ hit_time = time.time()
+ while True:
+ if msvcrt.kbhit():
+ msvcrt.getch()
+ break
+ if float(time.time() - hit_time) > timeout:
+ return
+ except:
+ msg("\nUser interrupt")
+ sys.exit(1)
+
+
def get_keypress_mswin(prompt="",immed_chars=""):
msg_r(prompt)
@@ -88,10 +125,12 @@ try:
import tty, termios
from select import select
get_char = get_keypress_unix
+ kb_hold_protect = kb_hold_protect_unix
except:
try:
import msvcrt, time
get_char = get_keypress_mswin
+ kb_hold_protect = kb_hold_protect_mswin
except:
if not sys.platform.startswith("linux") \
and not sys.platform.startswith("win"):
@@ -102,14 +141,35 @@ except:
sys.exit(2)
-def my_raw_input(prompt,echo=True):
+def my_raw_input(prompt,echo=True,allowed_chars=""):
+ try:
+ if echo:
+ reply = raw_input(prompt)
+ else:
+ from getpass import getpass
+ reply = getpass(prompt)
+ except:
+ print "\nUser interrupt"
+ sys.exit(1)
+
+ kb_hold_protect()
+ return reply
+
+
+def my_raw_input_old(prompt,echo=True,allowed_chars=""):
msg_r(prompt)
reply = ""
while True:
ch = get_char(immed_chars="ALL_EXCEPT_ENTER")
- if echo: msg_r(ch)
+ if allowed_chars and ch not in allowed_chars+"\n\r\b"+chr(0x7f):
+ continue
+ if echo:
+ if ch in "\b"+chr(0x7f): # WIP
+ pass
+ # reply.pop(0)
+ else: msg_r(ch)
if ch in "\n\r":
if not echo: msg("")
break
@@ -119,8 +179,8 @@ def my_raw_input(prompt,echo=True):
def _get_hash_params(hash_preset):
- if hash_preset in hash_presets:
- return hash_presets[hash_preset] # N,p,r,buflen
+ if hash_preset in g.hash_presets:
+ return g.hash_presets[hash_preset] # N,p,r,buflen
else:
# Shouldn't be here
msg("%s: invalid 'hash_preset' value" % hash_preset)
@@ -131,8 +191,8 @@ def show_hash_presets():
fs = " {:<7} {:<6} {:<3} {}"
msg("Available parameters for scrypt.hash():")
msg(fs.format("Preset","N","r","p"))
- for i in sorted(hash_presets.keys()):
- msg(fs.format("'%s'" % i, *hash_presets[i]))
+ for i in sorted(g.hash_presets.keys()):
+ msg(fs.format("'%s'" % i, *g.hash_presets[i]))
msg("N = memory usage (power of two), p = iterations (rounds)")
sys.exit(0)
@@ -142,7 +202,7 @@ cmessages = {
'unencrypted_secret_keys': """
This program generates secret keys from your {} seed, outputting them in
UNENCRYPTED form. Generate only the key(s) you need and guard them carefully.
-""".format(proj_name),
+""".format(g.proj_name),
'brain_warning': """
############################## EXPERTS ONLY! ##############################
@@ -275,7 +335,7 @@ def parse_address_list(arg,sep=","):
return False
for k in range(beg,end+1): ret.append(k)
else:
- msg("'%s': invalid argument for address range" % j)
+ msg("'%s': invalid argument for address range" % i)
return False
return sorted(set(ret))
@@ -288,10 +348,10 @@ def get_new_passphrase(what, opts):
elif 'echo_passphrase' in opts:
pw = " ".join(_get_words_from_user(("Enter %s: " % what), opts))
else:
- for i in range(passwd_max_tries):
+ for i in range(g.passwd_max_tries):
pw = " ".join(_get_words_from_user(("Enter %s: " % what),opts))
pw2 = " ".join(_get_words_from_user(("Repeat %s: " % what),opts))
- if debug: print "Passphrases: [%s] [%s]" % (pw,pw2)
+ if g.debug: print "Passphrases: [%s] [%s]" % (pw,pw2)
if pw == pw2:
msg("%ss match" % what.capitalize())
break
@@ -299,7 +359,7 @@ def get_new_passphrase(what, opts):
msg("%ss do not match" % what.capitalize())
else:
msg("User failed to duplicate passphrase in %s attempts" %
- passwd_max_tries)
+ g.passwd_max_tries)
sys.exit(2)
if pw == "": msg("WARNING: Empty passphrase")
@@ -321,9 +381,9 @@ def _get_from_brain_opt_params(opts):
def _get_seed_from_brain_passphrase(words,opts):
bp = " ".join(words)
- if debug: print "Sanitized brain passphrase: %s" % bp
+ if g.debug: print "Sanitized brain passphrase: %s" % bp
seed_len,hash_preset = _get_from_brain_opt_params(opts)
- if debug: print "Brainwallet l = %s, p = %s" % (seed_len,hash_preset)
+ if g.debug: print "Brainwallet l = %s, p = %s" % (seed_len,hash_preset)
msg_r("Hashing brainwallet data. Please wait...")
# Use buflen arg of scrypt.hash() to get seed of desired length
seed = _scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
@@ -334,7 +394,7 @@ def _get_seed_from_brain_passphrase(words,opts):
def encrypt_seed(seed, key, opts):
"""
Encrypt a seed for a {} deterministic wallet
- """.format(proj_name)
+ """.format(g.proj_name)
# 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
from Crypto.Cipher import AES
@@ -371,7 +431,7 @@ def write_to_stdout(data, what, confirm=True):
def get_default_wordlist():
- wl_id = default_wl
+ wl_id = g.default_wl
if wl_id == "electrum": from mmgen.mn_electrum import electrum_words as wl
elif wl_id == "tirosh": from mmgen.mn_tirosh import tirosh_words as wl
return wl.strip().split("\n")
@@ -411,7 +471,7 @@ def write_to_file(outfile,data,confirm=False):
def write_seed(seed, opts):
- outfile = "%s.%s" % (make_chksum_8(seed).upper(),seed_ext)
+ outfile = "%s.%s" % (make_chksum_8(seed).upper(),g.seed_ext)
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
@@ -432,7 +492,7 @@ def write_seed(seed, opts):
def write_mnemonic(mn, seed, opts):
- outfile = "%s.%s" % (make_chksum_8(seed).upper(),mn_ext)
+ outfile = "%s.%s" % (make_chksum_8(seed).upper(),g.mn_ext)
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
@@ -492,7 +552,7 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
hash_preset = opts['hash_preset']
- outfile="{}-{}[{},{}].{}".format(seed_id,key_id,seed_len,hash_preset,wallet_ext)
+ outfile="{}-{}[{},{}].{}".format(seed_id,key_id,seed_len,hash_preset,g.wallet_ext)
if 'outdir' in opts:
outfile = "%s/%s" % (opts['outdir'], outfile)
@@ -538,7 +598,7 @@ def compare_checksums(chksum1, desc1, chksum2, desc2):
msg("OK (%s)" % chksum1.upper())
return True
else:
- if debug:
+ if g.debug:
msg("ERROR!\nComputed checksum %s (%s) doesn't match checksum %s (%s)" \
% (desc1,chksum1,desc2,chksum2))
return False
@@ -552,7 +612,7 @@ def _is_hex(s):
def _check_mmseed_format(words):
valid = False
- what = "%s data" % seed_ext
+ what = "%s data" % g.seed_ext
try:
chklen = len(words[0])
except:
@@ -596,14 +656,14 @@ def _check_chksum_6(chk,val,desc,infile):
msg("%s checksum incorrect in file '%s'!" % (desc,infile))
msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
sys.exit(2)
- elif debug:
+ elif g.debug:
msg("%s checksum passed: %s" % (desc.capitalize(),chk))
def get_data_from_wallet(infile,opts,silent=False):
if not silent:
- msg("Getting {} wallet data from file '{}'".format(proj_name,infile))
+ msg("Getting {} wallet data from file '{}'".format(g.proj_name,infile))
f = open_file_or_exit(infile, 'r')
@@ -646,7 +706,7 @@ def _get_words_from_user(prompt, opts):
# split() also strips
words = my_raw_input(prompt,
echo=True if 'echo_passphrase' in opts else False).split()
- if debug: print "Sanitized input: [%s]" % " ".join(words)
+ if g.debug: print "Sanitized input: [%s]" % " ".join(words)
return words
@@ -656,15 +716,25 @@ def _get_words_from_file(infile,what):
# split() also strips
words = f.read().split()
f.close()
- if debug: print "Sanitized input: [%s]" % " ".join(words)
+ if g.debug: print "Sanitized input: [%s]" % " ".join(words)
return words
-def get_lines_from_file(infile,what=""):
+def get_lines_from_file(infile,what="",remove_comments=False):
if what != "": msg("Getting %s from file '%s'" % (what,infile))
f = open_file_or_exit(infile,'r')
lines = f.read().splitlines(); f.close()
- return lines
+ if remove_comments:
+ import re
+ # re.sub(pattern, repl, string, count=0, flags=0)
+ ret = []
+ for i in lines:
+ i = re.sub('#.*','',i,1)
+ i = re.sub('\s+$','',i)
+ if i: ret.append(i)
+ return ret
+ else:
+ return lines
def get_data_from_file(infile,what="data"):
@@ -678,14 +748,14 @@ def get_data_from_file(infile,what="data"):
def _get_seed_from_seed_data(words):
if not _check_mmseed_format(words):
- msg("Invalid %s data" % seed_ext)
+ msg("Invalid %s data" % g.seed_ext)
return False
stored_chk = words[0]
seed_b58 = "".join(words[1:])
chk = make_chksum_6(seed_b58)
- msg_r("Validating %s checksum..." % seed_ext)
+ msg_r("Validating %s checksum..." % g.seed_ext)
if compare_checksums(chk, "from seed", stored_chk, "from input"):
seed = b58decode_pad(seed_b58)
@@ -693,10 +763,10 @@ def _get_seed_from_seed_data(words):
msg("Invalid b58 number: %s" % val)
return False
- msg("%s data produces seed ID: %s" % (seed_ext,make_chksum_8(seed)))
+ msg("%s data produces seed ID: %s" % (g.seed_ext,make_chksum_8(seed)))
return seed
else:
- msg("Invalid checksum for {} seed".format(proj_name))
+ msg("Invalid checksum for {} seed".format(g.proj_name))
return False
@@ -730,7 +800,7 @@ def get_bitcoind_passphrase(prompt,opts):
def get_seed_from_wallet(
infile,
opts,
- prompt="Enter {} wallet passphrase: ".format(proj_name),
+ prompt="Enter {} wallet passphrase: ".format(g.proj_name),
silent=False
):
@@ -774,7 +844,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
if compare_checksums(chk,"of decrypted seed",seed_id,"in header"):
msg("Passphrase is OK")
else:
- if not debug:
+ if not g.debug:
msg_r("Checking key ID...")
chk = make_chksum_8(key)
if compare_checksums(chk, "of key", key_id, "in header"):
@@ -784,7 +854,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
return False
- if debug: msg("key: %s" % hexlify(key))
+ if g.debug: msg("key: %s" % hexlify(key))
return dec_seed
@@ -800,17 +870,17 @@ def get_seed(infile,opts,silent=False):
ext = infile.split(".")[-1]
- if ext == mn_ext: source = "mnemonic"
- elif ext == brain_ext: source = "brainwallet"
- elif ext == seed_ext: source = "seed"
- elif ext == wallet_ext: source = "wallet"
+ if ext == g.mn_ext: source = "mnemonic"
+ elif ext == g.brain_ext: source = "brainwallet"
+ elif ext == g.seed_ext: source = "seed"
+ elif ext == g.wallet_ext: source = "wallet"
elif 'from_mnemonic' in opts: source = "mnemonic"
elif 'from_brain' in opts: source = "brainwallet"
elif 'from_seed' in opts: source = "seed"
else:
if infile: msg(
"Invalid file extension for file: %s\nValid extensions: '.%s'" %
- (infile, "', '.".join(seed_exts)))
+ (infile, "', '.".join(g.seedfile_exts)))
else: msg("No seed source type specified and no file supplied")
sys.exit(2)
@@ -827,19 +897,19 @@ def get_seed(infile,opts,silent=False):
if 'quiet' not in opts:
confirm_or_exit(
cmessages['brain_warning'].format(
- proj_name.capitalize(), *_get_from_brain_opt_params(opts)),
+ g.proj_name.capitalize(), *_get_from_brain_opt_params(opts)),
"continue")
prompt = "Enter brainwallet passphrase: "
words = _get_words(infile,"brainwallet data",prompt,opts)
seed = _get_seed_from_brain_passphrase(words,opts)
elif source == "seed":
- prompt = "Enter seed in %s format: " % seed_ext
+ prompt = "Enter seed in %s format: " % g.seed_ext
words = _get_words(infile,"seed data",prompt,opts)
seed = _get_seed_from_seed_data(words)
elif source == "wallet":
seed = get_seed_from_wallet(infile, opts, silent=silent)
- if infile and not seed:
+ if infile and not seed and (source == "seed" or source == "mnemonic"):
msg("Invalid %s file: %s" % (source,infile))
sys.exit(2)
@@ -854,18 +924,6 @@ def get_seed_retry(infile,opts):
if seed: return seed
-def remove_blanks_comments(lines):
- import re
-# re.sub(pattern, repl, string, count=0, flags=0)
- ret = []
- for i in lines:
- i = re.sub('#.*','',i,1)
- i = re.sub('\s+$','',i)
- if i: ret.append(i)
-
- return ret
-
-
def do_pager(text):
pagers = ["less","more"]
@@ -905,6 +963,5 @@ def do_pager(text):
break
else: print text+end
-
if __name__ == "__main__":
print "utils.py"
diff --git a/setup.py b/setup.py
index ed22dd2f..48c26fe6 100755
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@ from distutils.core import setup
setup(
name = 'mmgen',
- version = '0.6.8',
+ version = '0.6.7',
author = 'Philemon',
author_email = 'mmgen-py@yandex.com',
url = 'https://github.com/mmgen/mmgen',