Browse Source

Old non-oo wallet code and files removed
test/test.py updated

philemon 10 years ago
parent
commit
acd8eb26c5

+ 204 - 166
doc/wiki/using-mmgen/Getting-Started-with-MMGen.md

@@ -12,6 +12,7 @@
 * <a href=#11>Using the mnemonic and seed features</a>
 * <a href=#12>Mnemonics and seeds — additional information</a>
 * <a href=#13>Incognito wallets</a>
+	* <a href=#13a>Hidden incognito wallets</a>
 
 
 ### <a name=01>Basic Operations</a>
@@ -22,7 +23,7 @@ On your offline computer, generate a wallet with a random seed:
 
 		$ mmgen-walletgen
 		...
-		Wallet saved to file '89ABCDEF-76543210[256,3].mmdat'
+		MMGen wallet written to file '89ABCDEF-76543210[256,3].mmdat'
 
 "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
@@ -41,7 +42,7 @@ Now generate ten addresses with your just-created wallet:
 
 		$ mmgen-addrgen 89ABCDEF-76543210[256,3].mmdat 1-10
 		...
-		Address data saved to file '89ABCDEF[1-10].addrs'
+		Addresses written to file '89ABCDEF[1-10].addrs'
 
 		$ cat '89ABCDEF[1-10].addrs'
 		89ABCDEF {
@@ -58,7 +59,7 @@ Now generate ten addresses with your just-created wallet:
 		}
 
 Note that the address range, "1-10", is reflected in the resulting filename.
-MMGen addresses are identified by their seed ID and index number, separated by a
+MMGen addresses are identified by their Seed ID and index 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.
@@ -93,9 +94,9 @@ Copy this file onto a USB stick and transfer it to your online computer.
 #### <a name=04>Import addresses (online computer):</a>
 
 On your online computer, go to your bitcoind data directory and move any
-existing 'wallet.dat' file out of the way.  Start bitcoind and let it generate
-a new 'wallet.dat', which you'll use as your **tracking wallet**.  Import your
-four addresses into the new tracking wallet with the command:
+existing 'wallet.dat' file out of harm's way.  Start bitcoind and let it
+generate a new 'wallet.dat', which you'll use as your **tracking wallet**.
+Import your four addresses into the new tracking wallet with the command:
 
 		$ mmgen-addrimport my.addrs
 
@@ -138,7 +139,7 @@ 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.
 
-First of all you'll want to examine your balances.  Note that 'mmgen-tool
+First of all, you'll want to examine your balances.  Note that 'mmgen-tool
 listaddresses' shows only MMGen address balances; to view **all** balances,
 including your non-MMGen ones, use the 'mmgen-txcreate' command:
 
@@ -147,8 +148,8 @@ including your non-MMGen ones, use the 'mmgen-txcreate' command:
 A list of all 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.  For a wallet with ten
-unspent outputs, the display might look something like this:
+The list may optionally be viewed in a pager or printed to file.  For a wallet
+with ten unspent outputs, the display might look something like this:
 
 		UNSPENT OUTPUTS (sort order: reverse amount)  Total BTC: 39.72
 		 Num  TX id  Vout    Address                            Amount (BTC)  Age(days)
@@ -168,126 +169,143 @@ unspent outputs, the display might look something like this:
 		(Type 'q' to quit sorting, 'p' to print to file, 'v' to view in pager):
 
 Now let's actually create a transaction.  Let's say you've decided to gradually
-begin moving your 39.72 BTC balance into your shiny new MMGen wallet with seed
+begin moving your 39.72 BTC balance into your shiny new MMGen wallet with Seed
 ID 89ABCDEF.
 
 Before moving any funds into your MMGen wallet, you should back it up 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 access to one of your backups can easily steal your coins.
+example.  You're advised to use a passphrase on your wallet.  Otherwise, anyone
+who gains physical access to one of your backups can easily steal your coins.
 
 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.  So you've decided to begin by breaking up the
-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:2, 89ABCDEF:3 and 89ABCDEF:4).
+small balances is a Good Idea.  So you've decided to begin by breaking up your
+address with the largest balance, 10 BTC, address 1F93Znz..., into three roughly
+equal parts and send them to the addresses labeled "Storage 1", "Storage 2" and
+"Storage 3" (89ABCDEF:2, 89ABCDEF:3 and 89ABCDEF:4).
 
-To refresh your memory, here are the three addresses in question:
+To refresh your memory, here are the three destination addresses in question:
 
-		$ cat my.addrs
+		$ cat my.addrs | grep -v Donations
 		# My first MMGen addresses
 		89ABCDEF {
-		  1    16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE  Donations
 		  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 10 BTC input to the third, subtracting a default
-transaction fee of 0.001 BTC:
+The following command does just this, sending 6.6 BTC of the transaction's 10
+BTC input to the first two addresses and the remainder to the third address,
+for which no amount has been specified:
 
 		$ mmgen-txcreate 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc,3.3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N,3.3 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s
 
-The bare address with no amount is the **change address**.  MMGen will compute
-the change amount (3.399 BTC in this case) automatically.
+The address with no amount is the **change address**; MMGen will calculate
+the amount sent to it automatically by subtracting the sum of the outputs
+plus transaction fee, if any, from the inputs.  In our example, 3.39995 BTC (10
+BTC - (3.3 + 3.3 BTC) - .00005 BTC default transaction fee) will go to this
+address.
 
-Note that the above transaction can be written much more elegantly and concisely
-using MMGen addresses in place of their Bitcoin equivalents:
+Note that the above transaction can be expressed much more concisely by
+replacing the Bitcoin addresses with their MMGen equivalents:
 
 		$ mmgen-txcreate 89ABCDEF:2,3.3 89ABCDEF:3,3.3 89ABCDEF:4
 
-After hitting ENTER you'll be presented with the same display produced by the
-'-i' option above, plus an interactive menu.  After quitting with 'q', you'll
-be prompted to choose the transaction's inputs.
+For this to work, the addresses must be imported into your tracking wallet,
+which they should be in any case.
+
+After hitting ENTER you'll be presented with the same UNSPENT OUTPUTS display as
+with the '-i' option above.  In our example, note that the output with 10 BTC
+which you wish to spend, 1F93Znz..., is listed as number '1'.  Remember this.
+After quitting the menu with 'q' you'll see the following prompt:
 
 		Enter a range or space-separated list of outputs to spend:
 
-Find the input with the 10 BTC balance in the list.  This is input 1), so type
-'1' and ENTER.  After several more prompts and confirmations 'mmgen-txcreate'
-will exit with the message:
+Type your remembered '1' here and hit ENTER.  After several more prompts and
+confirmations, your transaction will be saved:
 
-		Transaction data saved to file 'tx_1EDCBA[6.6].raw'
+		Transaction written to file 'tx_FEDCBA[6.6].raw'
 
-Note that the transaction has a unique ID, and the non-change spend amount, 6.6
-BTC, is conveniently included in the filename.
+Note that the transaction has a unique ID, and the non-change spend amount of
+6.6 BTC is included in the filename.
 
 #### <a name=06>Sign a transaction (offline computer):</a>
 
 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, 1F9495H8EJL....  If the key in question is in a
-bitcoin 'wallet.dat', there's an included command (a modified version of the
-well-known pywallet utility) that will conveniently extract it for you:
+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.
 
 		$ mmgen-pywallet -k wallet.dat
 		...
-		wallet.dat secret keys saved to file wd_EDBC983A[102].keys
+		Private keys written 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, since the unused keys will be ignored (you can extract only the
-keys you need using the '--keys-for-addrs' option).  Now go ahead and sign the
-transaction using this list of keys.
+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:
 
-		$ mmgen-txsign -k wd_EDBC983A[102].keys tx_1EDCBA[6.6].raw
+		$ mmgen-txsign -k wd_EDBC983A[102].keys tx_FEDCBA[6.6].raw
 		...
-		Signed transaction saved to file tx_ABCDEF[0.1].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 dump all their keys and
-merge them into a single file which you can use to sign all future transactions
-with wallet.dat inputs:
+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
 
-For your future transactions with MMGen address inputs, you'll list the MMGen
-seed source (wallet, mnemonic or seed file) on the command line after the
-transaction file, and the required keys will be generated automatically, as in
-this example:
+Once you've migrated your funds to MMGen, such key files will no longer be
+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:
 
-		$ mmgen-txsign tx_9D2C3A[1.23].raw B73B58EA-125FB230[256,3].mmdat
-		...
-		Signed transaction saved to file tx_9D2C3A[1.23].sig
+		$ mmgen-txsign tx_ABCDE[1.2345].raw 1234567A-BCDEF123[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.
+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:
 
-Eventually, when you've placed all your BTC under MMGen control, you'll never
-have deal with keys directly again, because MMGen generates all keys on the fly
-using the seed.
+		$ mmgen-txsign -k key_list my_tx.raw a.mmdat b.mmwords c.mmseed
 
 #### <a name=07>Send a transaction (online computer):</a>
 
-Now you're ready for the final step: broadcasting the transaction to the network.
-Copy the 'tx_*.sig' file to your online computer, start bitcoind, if it's not
-running, and execute the command:
+Now you're ready for the final step: broadcasting the transaction to the
+network.  Copy just-created signed transaction file to your online computer,
+start bitcoind and issue the command:
+
+		$ mmgen-txsend tx_FEDCBA[6.6].sig
+
+Like all MMGen commands, 'mmgen-txsend' is interactive, so you'll be prompted
+before the transaction is actually sent.
+
+Once the transaction is broadcast to the network, you can view your three new
+MMGen addresses and their balances:
 
-		$ mmgen-txsend tx_1EDCBA[6.6].sig
+		$ mmgen-tool listaddresses minconf=0
+		ADDRESS        COMMENT                            BALANCE
+		89ABCDEF:2     Storage 1                          3.3
+		89ABCDEF:3     Storage 2                          3.3
+		89ABCDEF:4     Storage 3                          3.39995
+
+Your total MMGen balance will also now be visible:
 
-Like all MMGen commands, 'mmgen-txsend' is interactive, so you'll be asked for
-confirmation before the transaction is actually sent.
+		$ mmgen-tool getbalance minconf=0
+		Wallet        Unconfirmed      <0 confirms      >=0 confirms
+		89ABCDEF:     0 BTC            0 BTC            9.99995 BTC
+		TOTAL:        0 BTC            0 BTC            9.99995 BTC
 
-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 to
-see them displayed in MMGen format.
+To verify that your transaction's received its first, second, third 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
-first funds under MMGen's control.
+first funds under MMGen control.
 
 ### <a name=10>Additional Features</a>
 
@@ -295,9 +313,9 @@ first funds under MMGen's control.
 
 Continuing our example above, generate a mnemonic from the wallet:
 
-		$ mmgen-walletchk -m '89ABCDEF-76543210[256,3].mmdat'
+		$ mmgen-walletconv -o words '89ABCDEF-76543210[256,3].mmdat'
 		...
-		Mnemonic data saved to file '89ABCDEF.mmwords'
+		Mnemonic data written to file '89ABCDEF.mmwords'
 
 		$ cat 89ABCDEF.mmwords
 		pleasure tumble spider laughter many stumble secret bother after search
@@ -309,114 +327,139 @@ words.  You may generate a wallet with 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.
+foreseeable future, it's advisable to 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, but using
 ordinary base conversion instead of Electrum's more complicated algorithm.
 
-Generate addresses 1-11 of seed 89ABCDEF using the mnemonic instead of the
-wallet:
+The mnemonic file may be used any place you'd use a MMGen wallet with the same
+Seed ID.  You can generate ten addresses with it just as you did with the
+wallet, for example:
 
-		$ mmgen-addrgen 89ABCDEF.mmwords 1-11
+		$ mmgen-addrgen 89ABCDEF.mmwords 1-10
 		...
-		Address data saved to file '89ABCDEF[1-11].addrs'
+		Address data written to file '89ABCDEF[1-10].addrs'
 
-Compare the first ten addresses with those earlier generated by the wallet.
-You'll see they're the same.
+The resulting address file will be identical to one generated by any wallet with
+Seed ID '89ABCDEF'.
 
-Regenerate a lost wallet using the mnemonic:
+The mnemonic can be used to regenerate a lost wallet:
 
-		$ mmgen-walletgen 89ABCDEF.mmwords
+		$ mmgen-walletconv 89ABCDEF.mmwords
 		...
-		Wallet saved to file '89ABCDEF-01234567[256,3].mmdat'
+		MMGen wallet written 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 mnemonic files are.
+Seed files bear the extension '.mmseed' and are generated and used exactly
+the same way as mnemonic files:
+
+		$ mmgen-walletconv -o seed '89ABCDEF-76543210[256,3].mmdat'
+		...
+		Seed data written to file '89ABCDEF.mmseed'
+
+And they can also be used to regenerate a wallet:
+
+		$ mmgen-walletconv 89ABCDEF.mmseed
+		...
+		MMGen wallet written to file '89ABCDEF-23456701[256,3].mmdat'
 
-A seed file for a 256-bit seed looks like this:
+Here's a sample seed file for a 256-bit wallet:
 
 		$ cat 8B7392ED.mmseed
 		f4c84b C5ZT wWpT Jsoi wRVw 2dm9 Aftd WLb8 FggQ eC8h Szjd da9L
 
-And for a 128-bit seed:
+And for a 128-bit wallet:
 
 		$ cat 8E0DFB78.mmseed
 		0fe02f XnyC NfPH piuW dQ2d nM47 VU
 
-As you can see, the latter file is short enough to be memorized or written down
-on a scrap of paper.  From the unix command line, you can test your memory using
-the seed's checksum ("0fe02f" in this example) as follows:
+As you can see, seed files are short enough to be easily written out by hand or
+even memorized.  And their built-in checksum makes it easy to test your memory
+using a simple Unix shell command:
 
-		$ echo -n XnyC NfPH piuW dQ2d nM47 VU | tr -d ' ' |sha256sum |cut -c 1-6
+		$ echo -n XnyC NfPH piuW dQ2d nM47 VU | tr -d ' '| sha256sum | cut -c 1-6
 		0fe02f
 
-Or better yet, use 'mmgen-tool' to do the same thing:
+Or you can do the same thing with 'mmgen-tool':
 
 		$ mmgen-tool str2id6 'XnyC NfPH piuW dQ2d nM47 VU'
 		0fe02f
 
 #### <a name=12>Mnemonics and seeds — additional information:</a>
 
-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.
+MMGen commands that take mnemonic and seed data may receive the data from a
+prompt instead of a file.  Just omit the file name and specify the input format:
 
-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 has
-intentionally been 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.
+		$ mmgen-walletconv -i words
+		...
+		Enter mnemonic data: <type or paste your mnemonic here>
+
+With the '-S' option, MMGen commands may be requested to print wallet data to
+screen instead of a file.  To safeguard against over-the-shoulder, Van Eck
+phreaking and other side-channel attacks, you'll be prompted before this
+sensitive data is actually displayed.  MMGen never prints unencrypted private
+data to screen by default.
 
 The output of any MMGen command may be written to a directory of your choice
 using the '-d' option.  For example, on a Linux system you can 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.
+'-d /dev/shm' to write keys and seeds to volatile memory instead of disk,
+ensuring that no trace of this sensitive data remains once your computer's been
+powered down.
 
 #### <a name=13><a name=incog>Incognito wallets</a>
 
-A wallet exported to incognito format is indistinguishable from random data,
-allowing you to hide your wallet at an offset within a random-filled file or
-partition.  Thus both the location and nature of the data are unknown to a
-potential attacker, who in addition cannot be sure that the file or partition
-contains anything useful at all, barring any inside knowledge.
+An incognito format wallet is indistinguishable from random data, allowing you
+to hide your wallet at an offset within a random-data-filled file or partition.
+Barring any inside knowledge, a potential attacker has no way of knowing where
+the wallet is hidden, or whether the file or partition contains anything of
+interest at all for that matter.
 
 An incognito wallet with a reasonably secure password could even be hidden on
 unencrypted cloud storage.  Hiding your wallet at some offset in a 1 GB file
 increases the difficulty of any attack by a factor of one billion, assuming
-a potential attacker even knows or suspects you have an MMGen wallet hidden
-there.
+again that any potential attacker even knows or suspects you have an MMGen
+wallet hidden there.
 
 If you plan to store your incognito wallet in an insecure location such as cloud
-storage, you're advised to use a strong scrypt preset and a strong password.
-These can be changed using the 'mmgen-passchg' utility:
+storage, you're advised to use a strong scrypt (hash) preset and a strong
+password.  These can be changed using the 'mmgen-passchg' utility:
 
 		$ mmgen-passchg -p 5 89ABCDEF-01234567[256,3].mmdat
 		...
-		Hash preset has changed (3 -> 5)
-		Enter new passphrase: <my new strong passphrase>
+		Hash preset of wallet: '3'
+		Enter old passphrase for MMGen wallet: <old weak passphrase>
 		...
-		Wallet saved to file '89ABCDEF-87654321[256,5].mmdat'
+		Hash preset changed to '5'
+		Enter new passphrase for MMGen wallet: <new strong passphrase>
+		...
+		MMGen wallet written to file '89ABCDEF-87654321[256,5].mmdat'
 
-The new scrypt preset is indicated by the numeral '5' after the comma in the new
-wallet filename.  Now export your new strengthened wallet to incognito format:
+The scrypt preset is the numeral in the wallet filename following the seed
+length.  As you can see, it's now changed to '5'.  Now export your new toughened
+wallet to incognito format, using the '-k' option to leave the passphrase
+unchanged:
 
-		$ mmgen-walletchk -g 89ABCDEF-87654321[256,5].mmdat
+		$ mmgen-walletconv -k -o incog 89ABCDEF-87654321[256,5].mmdat
+		...
+		Reusing passphrase at user request
 		...
-		Incognito wallet data saved to file '89ABCDEF-87654321-ECA86420[256,5].mmincog'
+		New Incog Wallet ID: ECA86420
+		...
+		Incognito data written to file '89ABCDEF-87654321-ECA86420[256,5].mmincog'
 
-'ECA86420' is the Incog ID.  This can be used by the 'mmgen-tool' utility to
-search through a file or partition and locate your wallet if you've forgotten
-where you hid it (see below).
+Incog wallets have a special identifier, the Incog ID, which can be used to
+locate the wallet data if you've forgotten where you hid it (see the example
+below).  Naturally, an attacker could use this ID to find the data too, so it
+should be kept secret.
 
-Repeat the same export operation, but output to hexadecimal:
+Incog wallets can also be output to hexadecimal format:
 
-		$ mmgen-walletchk -X 89ABCDEF-87654321[256,5].mmdat
+		$ mmgen-walletconv -k -o incox 89ABCDEF-87654321[256,5].mmdat
 		...
-		Incognito wallet data saved to file '89ABCDEF-87654321-CA86420E[256,5].mmincox'
+		Hex incognito data written to file '89ABCDEF-87654321-CA86420E[256,5].mmincox'
 
 		$ cat 89ABCDEF-87654321-1EE402F4[256,5].mmincox
 		6772 edb2 10cf ad0d c7dd 484b cc7e 42e9
@@ -424,72 +467,67 @@ Repeat the same export operation, but output to hexadecimal:
 		3706 c5ce 56e0 7590 e677 6c6e 750a d057
 		b43a 21f9 82c7 6bd1 fe96 bad9 2d54 c4c0
 
-Note that the Incog ID is different here: it's generated from the init vector,
+Note that the Incog ID is different here: it's generated from an init vector,
 which is a different random number each time, making the incog data as a whole
 different as well.  This allows you to store your incog data in multiple
-insecure locations without having repeated "random" wallet data give you away.
+public locations without having repeated "random" wallet data give you away.
 
-As you can see, this data is ideally suited for a paper wallet.  Just print it
-out on a printer and you're ready to go.
+Indistinguishable from any random hex dump, this data is ideally suited for a
+paper wallet that could potentially fall into the wrong hands.
 
 Your incognito wallet (whether hex or binary) can be used just like any other
-MMGen wallet, mnemonic or seed file.  Generate addresses with it like this:
+MMGen wallet, mnemonic or seed file to generate addresses and sign transactions:
 
-		$ mmgen-addrgen 89ABCDEF-87654321-CA86420E[256,5].mmincox 100-110
+		$ mmgen-addrgen 89ABCDEF-87654321-CA86420E[256,5].mmincox 101-110
 		...
 		Generated 10 addresses
-		Addresses written to file '89ABCDEF[100-110].addrs'
-
-
-Or sign a transaction like this:
+		Addresses written to file '89ABCDEF[101-110].addrs'
 
 		$ mmgen-txsign tx_FABCDE[0.3].raw 89ABCDEF-87654321-CA86420E[256,5].mmincox
 		...
-		Signed transaction saved to file tx_FABCDE[0.3].sig
+		Signed transaction written to file tx_FABCDE[0.3].sig
 
-You can create an incognito wallet and hide it at a specified offset in a file or
-partition in one convenient operation using the '-G' ('--export-incog-hidden')
-option. Here's how you'd hide a wallet in a 1GB file filled with random data.
-First create the random file:
+##### <a name=13a><a name=incog>Hidden incognito wallets</a>
 
-		$ dd if=/dev/urandom of=random.dat bs=1K count=1M
+With the '-o hincog' option, incognito wallet data can be created and hidden at
+a specified offset in a file or partition in a single convenient operation, with
+the random file being created automatically if required.  Here's how you'd
+create a 1GB file 'random.dat' and hide a wallet in it at offset 123456789:
 
-Or better yet, use 'mmgen-tool' to do the same job but with some additional user
-entropy and a progress meter:
-
-		$ mmgen-tool -r40 rand2file random.dat 1G
-
-Now export your wallet to hidden incognito format, hiding it in this 1GB random
-file at offset 123456789:
-
-		$ mmgen-walletchk -G random.dat,123456789 89ABCDEF-87654321[256,5].mmdat
+		$ mmgen-walletconv -k -o hincog -J random.dat,123456789 89ABCDEF-87654321[256,5].mmdat
+		...
+		New Incog Wallet ID: ED1F2ACB
+		...
+		Requested file 'random.dat' does not exist.  Create? (Y/n): Y
+		Enter file size: 1G
 		...
-		Incog ID: ED1F2ACB
 		Data written to file 'random.dat' at offset 123456789
 
-The altered random file can now be uploaded to a cloud storage service, for
-example, or some other, preferably non-public, location on the Net (in a
-real-life situation you'll choose a less obvious offset than '123456789'
-though, won't you?).
+Your "random" file can now be uploaded to a cloud storage service, for example,
+or some other, preferably non-public, location on the Net (in a real-life
+situation you will choose a less obvious offset than '123456789' though, won't
+you?).
 
-If at some point in the future you download this file to recover your wallet
-data but find you've forgotten the offset, you can recover it using the saved
-Incog ID as follows:
+Now let's say at some point in the future you download this file to recover
+your wallet and realize you've forgotten the offset where the data is hidden.
+If you've saved your Incog ID, you're in luck:
 
 		$ mmgen-tool find_incog_data random.dat ED1F2ACB
 		...
 		Incog data for ID ED1F2ACB found at offset 123456789
 
-Hidden incog wallets are almost as convenient to use as ordinary ones.
+The search process can be slow, so patience is required.  In addition, on
+large files 'false positives' are a distinct possibility, in which case you'll
+need to use the 'keep_searching=1' parameter to keep going until you find the
+real offset.
+
+Hidden incog wallets are nearly as convenient to use as ordinary ones.
 Generating ten addresses with your hidden incog data is as easy as this:
 
-		$ mmgen-addrgen -G random.dat,123456789,256 32-42
+		$ mmgen-addrgen -H random.dat,123456789 101-110
 
-Use the same syntax to sign a transaction:
+Transaction signing uses the same syntax:
 
-		$ mmgen-txsign -G random.dat,123456789,256 tx_ABCDEE[0.1].raw
+		$ mmgen-txsign -H random.dat,123456789 tx_ABCDEF[0.1].raw
 		...
-		Signed transaction saved to file tx_ABCDEE[0.1].sig
-
-Note that the seed length parameter here will always be '256' unless you're
-using a non-default seed length.
+		Signed transaction written to file 'tx_ABCDEF[0.1].sig'

+ 1 - 3
mmgen/addr.py

@@ -26,7 +26,6 @@ from hashlib import new as hashlib_new
 from binascii import hexlify, unhexlify
 
 from mmgen.bitcoin import numtowif
-# from mmgen.util import msg,qmsg,qmsg_r,make_chksum_N,get_lines_from_file,get_data_from_file,get_extension
 from mmgen.util import *
 from mmgen.tx import *
 from mmgen.obj import *
@@ -367,10 +366,9 @@ class AddrInfo(MMGenObject):
 
 
 	def make_addrdata_chksum(self):
-		nchars = 24
 		lines=[" ".join([str(e.idx),e.addr]+([e.wif] if self.has_keys else []))
 						for e in self.addrdata]
-		self.checksum = make_chksum_N(" ".join(lines), nchars, sep=True)
+		self.checksum = make_chksum_N(" ".join(lines), nchars=24, sep=True)
 
 
 	def fmt_data(self,enable_comments=False):

+ 17 - 233
mmgen/crypto.py

@@ -30,15 +30,6 @@ from mmgen.util import *
 from mmgen.term import get_char
 
 crmsg = {
-	'incog_iv_id': """
-   Check that the generated Incog ID above is correct.
-   If it's not, then your incognito data is incorrect or corrupted.
-""",
-	'incog_iv_id_hidden': """
-   Check that the generated Incog ID above is correct.
-   If it's not, then your incognito data is incorrect or corrupted,
-   or you've supplied an incorrect offset.
-""",
 	'usr_rand_notice': """
 You've chosen to not fully trust your OS's random number generator and provide
 some additional entropy of your own.  Please type %s symbols on your keyboard.
@@ -47,14 +38,23 @@ use both upper and lowercase as well as punctuation and numerals.  What you
 type will not be displayed on the screen.  Note that the timings between your
 keystrokes will also be used as a source of randomness.
 """,
-	'incorrect_incog_passphrase_try_again': """
-Incorrect passphrase, hash preset, or maybe old-format incog wallet.
-Try again? (Y)es, (n)o, (m)ore information:
-""".strip(),
-	'confirm_seed_id': """
-If the seed ID above is correct but you're seeing this message, then you need
-to exit and re-run the program with the '--old-incog-fmt' option.
-""".strip(),
+# 	'incog_iv_id': """
+#    Check that the generated Incog ID above is correct.
+#    If it's not, then your incognito data is incorrect or corrupted.
+# """,
+# 	'incog_iv_id_hidden': """
+#    Check that the generated Incog ID above is correct.
+#    If it's not, then your incognito data is incorrect or corrupted,
+#    or you've supplied an incorrect offset.
+# """,
+# 	'incorrect_incog_passphrase_try_again': """
+# Incorrect passphrase, hash preset, or maybe old-format incog wallet.
+# Try again? (Y)es, (n)o, (m)ore information:
+# """.strip(),
+# 	'confirm_seed_id': """
+# If the seed ID above is correct but you're seeing this message, then you need
+# to exit and re-run the program with the '--old-incog-fmt' option.
+# """.strip(),
 }
 
 def encrypt_seed(seed, key):
@@ -211,222 +211,6 @@ def get_random(length):
 		return os_rand
 
 
-def get_seed_from_wallet(
-		infile,
-		desc="{pnm} wallet".format(pnm=g.proj_name),
-		silent=False
-		):
-
-	wdata = get_data_from_wallet(infile,silent=silent)
-	label,metadata,hash_preset,salt,enc_seed = wdata
-
-	if opt.debug: display_control_data(*wdata)
-
-	padd = " "+infile if opt.quiet else ""
-	passwd = get_mmgen_passphrase(desc+padd)
-
-	key = make_key(passwd, salt, hash_preset)
-
-	return decrypt_seed(enc_seed, key, metadata[0], metadata[1])
-
-
-def get_hidden_incog_data():
-		# Already sanity-checked:
-		fname,offset,seed_len = opt.from_incog_hidden.split(",")
-		qmsg("Getting hidden incog data from file '%s'" % fname)
-
-		z = 0 if opt.old_incog_fmt else 8
-		dlen = g.aesctr_iv_len + g.salt_len + (int(seed_len)/8) + z
-
-		fsize = check_data_fits_file_at_offset(fname,int(offset),dlen,"read")
-
-		import os
-		f = os.open(fname,os.O_RDONLY)
-		os.lseek(f, int(offset), os.SEEK_SET)
-		data = os.read(f, dlen)
-		os.close(f)
-		qmsg("Data read from file '%s' at offset %s" % (fname,offset),
-				"Data read from file")
-		return data
-
-def confirm_old_format():
-
-	while True:
-		reply = get_char(
-			crmsg['incorrect_incog_passphrase_try_again']+" ").strip("\n\r")
-		if not reply:       msg(""); return False
-		elif reply in 'yY': msg(""); return False
-		elif reply in 'nN': msg("\nExiting at user request"); sys.exit(1)
-		elif reply in 'mM': msg(""); return True
-		else:
-			if opt.verbose: msg("\nInvalid reply")
-			else: msg_r("\r")
-
-
-def get_seed_from_incog_wallet(
-		infile,
-		desc="{pnm} incognito wallet".format(pnm=g.proj_name),
-		silent=False,
-		hex_input=False
-	):
-
-	desc = "incognito wallet data"
-
-	if opt.from_incog_hidden:
-		d = get_hidden_incog_data()
-	else:
-		d = get_data_from_file(infile,desc)
-		if hex_input:
-			try:
-				d = unhexlify("".join(d.split()).strip())
-			except:
-				msg("Data in file '%s' is not in hexadecimal format" % infile)
-				sys.exit(2)
-		# File could be of invalid length, so check:
-		z = 0 if opt.old_incog_fmt else 8
-		valid_dlens = [i/8 + g.aesctr_iv_len + g.salt_len + z for i in g.seed_lens]
-		# New fmt: [56, 64, 72]. Old fmt: [48, 56, 64].
-		if len(d) not in valid_dlens:
-			vn = [i/8 + g.aesctr_iv_len + g.salt_len + 8 for i in g.seed_lens]
-			if len(d) in vn:
-				msg("Re-run the program without the '--old-incog-fmt' option")
-				sys.exit()
-			else: qmsg(
-			"Invalid incognito file size: %s.  Valid sizes (in bytes): %s" %
-						(len(d), " ".join([str(i) for i in valid_dlens])))
-			return False
-
-	iv, enc_incog_data = d[0:g.aesctr_iv_len], d[g.aesctr_iv_len:]
-
-	incog_id = make_iv_chksum(iv)
-	msg("Incog ID: %s (IV ID: %s)" % (incog_id,make_chksum_8(iv)))
-	qmsg("Check the applicable value against your records")
-	vmsg(crmsg['incog_iv_id_hidden' if opt.from_incog_hidden
-			else 'incog_iv_id'])
-
-	while True:
-		passwd = get_mmgen_passphrase(desc+" "+incog_id)
-
-		qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets)))
-		if 'hash_preset' in opt.set_by_user:
-			hp = opt.hash_preset
-		else:
-			hp = get_hash_preset_from_user(desc="incog wallet")
-
-		# IV is used BOTH to initialize counter and to salt password!
-		key = make_key(passwd, iv, hp, "wrapper key")
-		d = decrypt_data(enc_incog_data, key, int(hexlify(iv),16), "incog data")
-
-		salt,enc_seed = d[0:g.salt_len], d[g.salt_len:]
-
-		key = make_key(passwd, salt, hp, "main key")
-		vmsg("Key ID: %s" % make_chksum_8(key))
-
-		seed = decrypt_seed(enc_seed, key, "", "")
-		old_fmt_sid = make_chksum_8(seed)
-
-		def confirm_correct_seed_id(sid):
-			m = "Seed ID: %s.  Is the seed ID correct?" % sid
-			return keypress_confirm(m, True)
-
-		if opt.old_incog_fmt:
-			if confirm_correct_seed_id(old_fmt_sid):
-				break
-		else:
-			chk,seed_maybe = seed[:8],seed[8:]
-			if sha256(seed_maybe).digest()[:8] == chk:
-				msg("Passphrase and hash preset are correct")
-				seed = seed_maybe
-				break
-			elif confirm_old_format():
-				if confirm_correct_seed_id(old_fmt_sid):
-					break
-
-	msg("Valid incog data for seed ID %s" % make_chksum_8(seed))
-	return seed
-
-
-def _get_seed(infile,silent=False,seed_id=""):
-
-	ext = get_extension(infile)
-
-	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 ext == g.incog_ext:        source = "incognito wallet"
-	elif ext == g.incog_hex_ext:    source = "incognito wallet"
-	elif opt.from_mnemonic : source = "mnemonic"
-	elif opt.from_brain    : source = "brainwallet"
-	elif opt.from_seed     : source = "seed"
-	elif opt.from_incog    : source = "incognito wallet"
-	else:
-		if infile: msg(
-			"Invalid file extension for file: %s\nValid extensions: '.%s'" %
-			(infile, "', '.".join(g.seedfile_exts)))
-		else: msg("No seed source type specified and no file supplied")
-		sys.exit(2)
-
-	seed_id_str = " for seed ID "+seed_id if seed_id else ""
-	if source == "mnemonic":
-		prompt = "Enter mnemonic%s: " % seed_id_str
-		words = get_words(infile,"mnemonic data",prompt)
-		wl = get_default_wordlist()
-		from mmgen.mnemonic import get_seed_from_mnemonic
-		seed = get_seed_from_mnemonic(words,wl)
-	elif source == "brainwallet":
-		if not opt.from_brain:
-			msg("'--from-brain' parameters must be specified for brainwallet file")
-			sys.exit(2)
-		prompt = "Enter brainwallet passphrase%s: " % seed_id_str
-		words = get_words(infile,"brainwallet data",prompt)
-		seed = _get_seed_from_brain_passphrase(words)
-	elif source == "seed":
-		prompt = "Enter seed%s in %s format: " % (seed_id_str,g.seed_ext)
-		words = get_words(infile,"seed data",prompt)
-		seed = get_seed_from_seed_data(words)
-	elif source == "wallet":
-		seed = get_seed_from_wallet(infile, silent=silent)
-	elif source == "incognito wallet":
-		h = (ext == g.incog_hex_ext) or opt.from_incog_hex
-		seed = get_seed_from_incog_wallet(infile, silent=silent, hex_input=h)
-
-
-	if infile and not seed and (
-		source == "seed" or source == "mnemonic" or source == "incognito wallet"):
-		msg("Invalid %s file '%s'" % (source,infile))
-		sys.exit(2)
-
-	dmsg("Seed: %s" % hexlify(seed))
-
-	return seed
-
-
-# Repeat if entered data is invalid
-def get_seed_retry(infile,seed_id=""):
-	silent = False
-	while True:
-		seed = _get_seed(infile,silent=silent,seed_id=seed_id)
-		silent = True
-		if seed: return seed
-
-
-def _get_seed_from_brain_passphrase(words,silent=False):
-	bp = " ".join(words)
-	dmsg("Sanitized brain passphrase: %s" % bp)
-	seed_len,hash_preset = get_from_brain_opt_params()
-	dmsg("Brainwallet l = %s, p = %s" % (seed_len,hash_preset))
-	vmsg_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)
-	vmsg("Done")
-
-	if not silent:
-		msg("Valid brainwallet for seed ID %s" % make_chksum_8(seed))
-
-	return seed
-
-
 # Vars for mmgen_*crypt functions only
 salt_len,sha256_len,nonce_len = 32,32,32
 

+ 11 - 4
mmgen/filename.py

@@ -25,7 +25,7 @@ from mmgen.util import die,get_extension,check_infile
 
 class Filename(MMGenObject):
 
-	def __init__(self,fn,ftype=None):
+	def __init__(self,fn,ftype=None,write=False):
 		self.name     = fn
 		self.dirname  = os.path.dirname(fn)
 		self.basename = os.path.basename(fn)
@@ -41,10 +41,17 @@ class Filename(MMGenObject):
 				die(2,"Unrecognized extension '.%s' for file '%s'" % (self.ext,fn))
 
 		# TODO: Check for Windows
+		mode = (os.O_RDONLY,os.O_RDWR)[int(write)]
 		import stat
 		if stat.S_ISBLK(os.stat(fn).st_mode):
-			fd = os.open(fn, os.O_RDONLY)
-			self.size = os.lseek(fd, 0, os.SEEK_END)
-			os.close(fd)
+			try:
+				fd = os.open(fn, mode)
+			except OSError as e:
+				if e.errno == 13:
+					die(2,"'%s': permission denied" % fn)
+#				if e.errno != 17: raise
+			else:
+				self.size = os.lseek(fd, 0, os.SEEK_END)
+				os.close(fd)
 		else:
 			self.size = os.stat(fn).st_size

+ 1 - 1
mmgen/globalvars.py

@@ -49,7 +49,7 @@ prog_name = os.path.basename(sys.argv[0])
 author    = "Philemon"
 email     = "<mmgen-py@yandex.com>"
 Cdates    = '2013-2015'
-version   = '0.8.0'
+version   = '0.8.1rc1'
 
 required_opts = [
 	"quiet","verbose","debug","outdir","echo_passphrase","passwd_file",

+ 3 - 8
mmgen/main.py

@@ -22,14 +22,9 @@ main.py - Script launcher for the MMGen suite
 
 def launch(what):
 
-	import os
-	t = "MMGEN_USE_OLD_SCRIPTS"
-	if not (t in os.environ and os.environ[t]):
-		if what in ("walletgen","walletchk","passchg"):
-			what = "wallet"
-
-	if what == "walletconv": what = "wallet"
-	if what == "keygen":     what = "addrgen"
+	if what in ("walletgen","walletchk","walletconv","passchg"):
+		what = "wallet"
+	if what == "keygen": what = "addrgen"
 
 	try: import termios
 	except: __import__("mmgen.main_" + what) # Windows

+ 67 - 98
mmgen/main_addrgen.py

@@ -28,48 +28,50 @@ import mmgen.opt as opt
 from mmgen.util import *
 from mmgen.crypto import *
 from mmgen.addr import *
+from mmgen.seed import SeedSource
 
 if sys.argv[0].split("-")[-1] == "keygen":
 	gen_what = "keys"
 	opt_filter = None
+	note1 = """
+By default, both addresses and secret keys are generated.
+""".strip()
 else:
 	gen_what = "addresses"
-	opt_filter = "hdceHKlpPqSvbgXGoms"
+	opt_filter = "hcdeiHOKlpzPqSv"
+	note1 = """
+If available, the external 'keyconv' program will be used for address
+generation.
+""".strip()
 
 opts_data = {
+	'sets': [('print_checksum',True,'quiet',True)],
 	'desc': """Generate a range or list of {what} from an {pnm} wallet,
                   mnemonic, seed or password""".format(what=gen_what,pnm=g.proj_name),
 	'usage':"[opts] [infile] <address range or list>",
 	'options': """
--h, --help              Print this help message
--A, --no-addresses      Print only secret keys, no addresses
--d, --outdir=       d   Specify an alternate directory 'd' for output
--c, --save-checksum     Save address list checksum to file
--e, --echo-passphrase   Echo passphrase or mnemonic to screen upon entry
--H, --show-hash-presets Show information on available hash presets
--K, --no-keyconv        Force use of internal libraries for address gener-
-                        ation, even if 'keyconv' is available
--l, --seed-len=     N   Length of seed.  Options: {seed_lens}
-                        (default: {g.seed_len})
--p, --hash-preset=  p   Use scrypt.hash() parameters from preset 'p' when
-                        hashing password (default: '{g.hash_preset}')
--P, --passwd-file=  f   Get {pnm} wallet passphrase from file 'f'
--q, --quiet             Suppress warnings; overwrite files without
-                        prompting
--S, --stdout            Print {what} to stdout
--v, --verbose           Produce more verbose output
--x, --b16               Print secret keys in hexadecimal too
-
--b, --from-brain=  l,p  Generate {what} from a user-created password,
-                        i.e. a "brainwallet", using seed length 'l' and
-                        hash preset 'p' (comma-separated)
--g, --from-incog        Generate {what} from an incognito wallet
--X, --from-incog-hex    Generate {what} from incognito hexadecimal wallet
--G, --from-incog-hidden=f,o,l Generate {what} from incognito data in file
-                        'f' at offset 'o', with seed length of 'l'
--o, --old-incog-fmt     Use old (pre-0.7.8) incog format
--m, --from-mnemonic     Generate {what} from an electrum-like mnemonic
--s, --from-seed         Generate {what} from a seed in .{g.seed_ext} format
+-h, --help            Print this help message.
+-A, --no-addresses    Print only secret keys, no addresses.
+-c, --print-checksum  Print address list checksum and exit.
+-d, --outdir=      d  Output files to directory 'd' instead of working dir.
+-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry.
+-i, --in-fmt=      f  Input is from wallet format 'f' (see FMT CODES below).
+-H, --hidden-incog-input-params=f,o  Read hidden incognito data from file
+                      'f' at offset 'o' (comma-separated).
+-O, --old-incog-fmt   Specify old-format incognito input.
+-K, --no-keyconv      Force use of internal libraries for address genera-
+                      tion, even if 'keyconv' is available.
+-l, --seed-len=    l  Specify wallet seed length of 'l' bits.  This option
+                      is required only for brainwallet and incognito inputs
+                      with non-standard (< {g.seed_len}-bit) seed lengths.
+-p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
+                      for password hashing (default: '{g.hash_preset}').
+-z, --show-hash-presets Show information on available hash presets.
+-P, --passwd-file= f  Get wallet passphrase from file 'f'.
+-q, --quiet           Produce quieter output; suppress some warnings.
+-S, --stdout          Print {what} to stdout.
+-v, --verbose         Produce more verbose output.
+-x, --b16             Print secret keys in hexadecimal too.
 """.format(
 	seed_lens=", ".join([str(i) for i in g.seed_lens]),
 	pnm=g.proj_name,
@@ -78,96 +80,63 @@ opts_data = {
 	'notes': """
 
 Addresses are given in a comma-separated list.  Hyphen-separated ranges are
-also allowed.{a}
-
-If available, the external 'keyconv' program will be used for address
-generation.
-
-Data for the --from-<what> options will be taken from <infile> if <infile>
-is specified.  Otherwise, the user will be prompted to enter the data.
-
-For passphrases all combinations of whitespace are equal, and leading and
-trailing space are ignored.  This permits reading passphrase data from a
-multi-line file with free spacing and indentation.  This is particularly
-convenient for long brainwallet passphrases, for example.
+also allowed.
 
-BRAINWALLET NOTE:
+{n}
 
-As brainwallets require especially strong hashing to thwart dictionary
-attacks, the brainwallet hash preset must be specified by the user, using
-the 'p' parameter of the '--from-brain' option
+{o.pw_note}
 
-The '--from-brain' option also requires the user to specify a seed length
-(the 'l' parameter)
+{o.bw_note}
 
-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(a="\n\nBy default, both addresses and secret keys are generated."
-				if gen_what == "keys" else "")
+FMT CODES:
+  {f}
+""".format(
+		n=note1,
+		f="\n  ".join(SeedSource.format_fmt_codes().split("\n")),
+		o=opt.opts
+	)
 }
 
-wmsg = {
-	'unencrypted_secret_keys': """
-This program generates secret keys from your {pnm} seed, outputting them in
-UNENCRYPTED form.  Generate only the key(s) you need and guard them carefully.
-""".format(pnm=g.proj_name),
-}
 
 cmd_args = opt.opts.init(opts_data,add_opts=["b16"],opt_filter=opt_filter)
 
-if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
-
-if len(cmd_args) == 1 and any([
-		opt.from_mnemonic,opt.from_brain,opt.from_seed,opt.from_incog_hidden]):
-	infile,addr_idx_arg = "",cmd_args[0]
-elif len(cmd_args) == 2:
-	infile,addr_idx_arg = cmd_args
-	check_infile(infile)
-else: opt.opts.usage()
-
-addr_idxs = parse_addr_idxs(addr_idx_arg)
+nargs = 2
+if len(cmd_args) < nargs \
+		and not opt.hidden_incog_input_params and not opt.in_fmt:
+	opt.opts.usage()
+elif len(cmd_args) > nargs \
+		or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
+			opt.opts.usage()
 
-if not addr_idxs: sys.exit(2)
+addrlist_arg = cmd_args.pop()
+addr_idxs = parse_addr_idxs(addrlist_arg)
+if not addr_idxs:
+	die(1,"'%s': invalid address list argument" % addrlist_arg)
 
 do_license_msg()
 
-# Interact with user:
-if gen_what == "keys" and not opt.quiet:
-	confirm_or_exit(wmsg['unencrypted_secret_keys'], 'continue')
+opt.gen_what = "a" if gen_what == "addresses" \
+					else "k" if opt.no_addresses else "ka"
 
 # Generate data:
+ss = SeedSource(*cmd_args)
 
-seed = get_seed_retry(infile)
-
-opt.gen_what = "a" if gen_what == "addresses" else (
-	"k" if opt.no_addresses else "ka")
-
-ainfo = generate_addrs(seed,addr_idxs)
+ainfo = generate_addrs(ss.seed.data,addr_idxs)
 
 addrdata_str = ainfo.fmt_data()
-outfile_base = "{}[{}]".format(make_chksum_8(seed), ainfo.idxs_fmt)
+outfile_base = "{}[{}]".format(ss.seed.sid, ainfo.idxs_fmt)
 
-if 'a' in opt.gen_what and opt.save_checksum:
-	w = "key-address" if 'k' in opt.gen_what else "address"
-	write_to_file(outfile_base+"."+g.addrfile_chksum_ext,
-		ainfo.checksum+"\n","%s data checksum" % w,True,True,False)
+if 'a' in opt.gen_what and opt.print_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 = ""
 
-# Output data:
-if opt.stdout or not sys.stdout.isatty():
-	if enc_ext and sys.stdout.isatty():
-		msg("Cannot write encrypted data to screen.  Exiting")
-		sys.exit(2)
-	write_to_stdout(addrdata_str,gen_what,ask_terminal=(gen_what=="keys"
-						and not opt.quiet and sys.stdout.isatty()))
-else:
-	outfile = "%s.%s%s" % (outfile_base, (
-		g.keyaddrfile_ext if "ka" in opt.gen_what else (
-		g.keyfile_ext if "k" in opt.gen_what else
-		g.addrfile_ext)), enc_ext)
-	write_to_file(outfile,addrdata_str,gen_what,not opt.quiet,True)
+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"
+write_data_to_file(outfile,addrdata_str,gen_what,ask_tty=ask_tty)

+ 0 - 124
mmgen/main_passchg.py

@@ -1,124 +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-passchg: Change an MMGen deterministic wallet's passphrase, label or
-               hash preset
-"""
-
-import sys
-from mmgen.util import *
-from mmgen.crypto import *
-import mmgen.globalvars as g
-import mmgen.opt as opt
-
-opts_data = {
-	'desc':  """Change the passphrase, hash preset or label of an {pnm}
-                  deterministic wallet""".format(pnm=g.proj_name),
-	'usage':   "[opts] [filename]",
-	'options': """
--h, --help                Print this help message
--d, --outdir=           d Specify an alternate directory 'd' for output
--H, --show-hash-presets   Show information on available hash presets
--k, --keep-old-passphrase Keep old passphrase (use when changing hash
-                          strength or label only)
--L, --label=            l Change the wallet's label to 'l'
--p, --hash-preset=      p Change scrypt.hash() parameters to preset 'p'
-                          (default: '{g.hash_preset}')
--P, --passwd-file=      f Get new {pnm} wallet passphrase from file 'f'
--r, --usr-randchars=    n Get 'n' characters of additional randomness from
-                          user (min={g.min_urandchars}, max={g.max_urandchars})
--q, --quiet               Suppress warnings; overwrite files without
-                          prompting
--v, --verbose             Produce more verbose output
-""".format(g=g,pnm=g.proj_name),
-	'notes': """
-
-NOTE: The key ID will change if either the passphrase or hash preset are
-      changed
-"""
-}
-
-cmd_args = opt.opts.init(opts_data)
-
-if len(cmd_args) != 1:
-	msg("One input file must be specified")
-	sys.exit(2)
-infile = cmd_args[0]
-
-# Old key:
-label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile)
-seed_id,key_id = metadata[:2]
-
-# Repeat on incorrect pw entry
-while True:
-	desc = "{pnm} wallet".format(pnm=g.proj_name)
-	passwd = get_mmgen_passphrase(desc,not opt.keep_old_passphrase)
-	key = make_key(passwd, salt, hash_preset)
-	seed = decrypt_seed(enc_seed, key, seed_id, key_id)
-	if seed: break
-
-changed = {}
-
-if opt.label:
-	if opt.label != label:
-		msg("Label changed: '%s' -> '%s'" % (label, opt.label))
-		changed['label'] = True
-	else:
-		msg("Label is unchanged: '%s'" % (label))
-else: opt.label = label  # Copy the old label
-
-if opt.hash_preset:
-	if hash_preset != opt.hash_preset:
-		qmsg("Hash preset changed: '%s' -> '%s'" %
-			(hash_preset, opt.hash_preset))
-		changed['preset'] = True
-	else:
-		msg("Hash preset is unchanged")
-else:
-	opt.hash_preset = hash_preset
-
-if opt.keep_old_passphrase:
-	msg("Keeping old passphrase by user request")
-else:
-	new_passwd = get_new_passphrase(
-			"{pnm} wallet".format(pnm=g.proj_name), True)
-
-	if new_passwd == passwd:
-		qmsg("Passphrase is unchanged")
-	else:
-		qmsg("Passphrase has changed")
-		passwd = new_passwd
-		changed['passwd'] = True
-
-if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
-	qmsg("Will update salt and key ID")
-
-	from hashlib import sha256
-
-	salt = sha256(salt + get_random(128)).digest()[:g.salt_len]
-	key = make_key(passwd, salt, opt.hash_preset)
-	new_key_id = make_chksum_8(key)
-	qmsg("Key ID changed: %s -> %s" % (key_id,new_key_id))
-	key_id = new_key_id
-	enc_seed = encrypt_seed(seed, key)
-elif not 'label' in changed:
-	msg("Data unchanged.  No file will be written")
-	sys.exit(2)
-
-write_wallet_to_file(seed, passwd, key_id, salt, enc_seed)

+ 50 - 41
mmgen/main_txsign.py

@@ -26,44 +26,50 @@ import mmgen.globalvars as g
 import mmgen.opt as opt
 from mmgen.tx import *
 from mmgen.util import do_license_msg,dmsg
+from mmgen.seed import SeedSource
+
 
 pnm = g.proj_name
 pnl = pnm.lower()
 
 opts_data = {
 	'desc':    "Sign Bitcoin transactions generated by {pnl}-txcreate".format(pnl=pnl),
-	'usage':   "[opts] <transaction file> .. [mmgen wallet/seed/words/brainwallet file] ..",
+	'usage':   "[opts] <transaction file>... [seed source]...",
 	'options': """
--h, --help               Print this help message
--d, --outdir=         d  Specify an alternate directory 'd' for output
--e, --echo-passphrase    Print passphrase to screen when typing it
--i, --info               Display information about the transaction and exit
--t, --terse-info         Like '--info', but produce more concise output
--I, --tx-id              Display transaction ID and exit
--k, --keys-from-file= f  Provide additional keys for non-{pnm} addresses
--K, --no-keyconv         Force use of internal libraries for address gener-
-                         ation, even if 'keyconv' is available
--M, --mmgen-keys-from-file=f  Provide keys for {pnm} addresses in a key-
-                         address file (output of '{pnl}-keygen'). Permits
-                         online signing without an {pnm} seed source.
-                         The key-address file is also used to verify
-                         {pnm}-to-BTC mappings, so its checksum should
-                         be recorded by the user.
--P, --passwd-file=    f  Get {pnm} wallet or bitcoind passphrase from file 'f'
--q, --quiet              Suppress warnings; overwrite files without
-                         prompting
--v, --verbose            Produce more verbose output
--b, --from-brain=    l,p Generate keys from a user-created password,
-                         i.e. a "brainwallet", using seed length 'l' and
-                         hash preset 'p'
--w, --use-wallet-dat     Get keys from a running bitcoind
--g, --from-incog         Generate keys from an incognito wallet
--X, --from-incog-hex     Generate keys from an incognito hexadecimal wallet
--G, --from-incog-hidden= f,o,l  Generate keys from incognito data in file
-                         'f' at offset 'o', with seed length of 'l'
--o, --old-incog-fmt      Use old (pre-0.7.8) incog format
--m, --from-mnemonic      Generate keys from an electrum-like mnemonic
--s, --from-seed          Generate keys from a seed in .{g.seed_ext} format
+-h, --help            Print this help message.
+-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brain-
+                      wallet input.  Required only if these parameters dif-
+                      fer from those of an incognito wallet also being used
+                      as a seed source.
+-d, --outdir=      d  Specify an alternate directory 'd' for output.
+-D, --tx-id           Display transaction ID and exit.
+-e, --echo-passphrase Print passphrase to screen when typing it.
+-i, --in-fmt=      f  Input is from wallet format 'f' (see FMT CODES below).
+-H, --hidden-incog-input-params=f,o  Read hidden incognito data from file
+                      'f' at offset 'o' (comma-separated).
+-O, --old-incog-fmt   Specify old-format incognito input.
+-l, --seed-len=    l  Specify wallet seed length of 'l' bits.  This option
+                      is required only for brainwallet and incognito inputs
+                      with non-standard (< {g.seed_len}-bit) seed lengths.
+-p, --hash-preset=p   Use the scrypt hash parameters defined by preset 'p'
+                      for password hashing (default: '{g.hash_preset}').
+-z, --show-hash-presets Show information on available hash presets.
+-k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
+-K, --no-keyconv      Force use of internal libraries for address gener-
+                      ation, even if 'keyconv' is available.
+-M, --mmgen-keys-from-file=f Provide keys for {pnm} addresses in a key-
+                      address file (output of '{pnl}-keygen'). Permits
+                      online signing without an {pnm} seed source.
+                      The key-address file is also used to verify
+                      {pnm}-to-BTC mappings, so its checksum should
+                      be recorded by the user.
+-P, --passwd-file= f  Get {pnm} wallet or bitcoind passphrase from file 'f'
+-q, --quiet           Suppress warnings; overwrite files without
+                      prompting
+-I, --info            Display information about the transaction and exit.
+-t, --terse-info      Like '--info', but produce more concise output.
+-v, --verbose         Produce more verbose output
+-w, --use-wallet-dat  Get keys from a running bitcoind
 """.format(g=g,pnm=pnm,pnl=pnl),
 	'notes': """
 
@@ -91,7 +97,13 @@ Seed data supplied in files must have the following extensions:
    seed:        '.{g.seed_ext}'
    mnemonic:    '.{g.mn_ext}'
    brainwallet: '.{g.brain_ext}'
-""".format(g=g,pnm=pnm,pnl=pnl)
+
+FMT CODES:
+  {f}
+""".format(
+		f="\n  ".join(SeedSource.format_fmt_codes().split("\n")),
+		g=g,pnm=pnm,pnl=pnl
+	)
 }
 
 wmsg = {
@@ -110,23 +122,20 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds):
 	if seed_id in saved_seeds.keys():
 		return saved_seeds[seed_id]
 
-	from mmgen.crypto import get_seed_retry
-
 	while True:
 		if infiles:
-			seed = get_seed_retry(infiles.pop(0))
-		elif any([opt.from_brain,opt.from_mnemonic,opt.from_seed,opt.from_incog]):
+			ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
+		elif opt.in_fmt:
 			qmsg("Need seed data for seed ID %s" % seed_id)
-			seed = get_seed_retry("",seed_id)
+			ss = SeedSource()
 			msg("User input produced seed ID %s" % make_chksum_8(seed))
 		else:
 			msg("ERROR: No seed source found for seed ID: %s" % seed_id)
 			sys.exit(2)
 
-		sid = make_chksum_8(seed)
-		saved_seeds[sid] = seed
+		saved_seeds[ss.seed.sid] = ss.seed.data
 
-		if sid == seed_id: return seed
+		if ss.seed.sid == seed_id: return ss.seed.data
 
 
 def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds):
@@ -285,7 +294,7 @@ def get_keys_from_keylist(kldata,other_addrs):
 
 infiles = opt.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()
 for i in infiles: check_infile(i)

+ 22 - 8
mmgen/main_wallet.py

@@ -33,10 +33,12 @@ usage = "[opts] [infile]"
 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 = "hdoJlLpPqrSv"
+	opt_filter = "ehdoJlLpPqrSvz"
 	usage = "[opts]"
 	oaction = "output"
 	nargs = 0
@@ -45,21 +47,25 @@ elif invoked_as == "conv":
 	opt_filter = None
 elif invoked_as == "chk":
 	desc = "Check validity of an {pnm} wallet"
-	opt_filter = "hiHOlpPqrv"
+	opt_filter = "ehiHOlpPqrvz"
 	iaction = "input"
 elif invoked_as == "passchg":
 	desc = "Change the password, hash preset or label of an {pnm} wallet"
-	opt_filter = "hdiHkKOlLmpPqrSv"
+	opt_filter = "ehdiHkKOlLmpPqrSvz"
 	iaction = "input"
+	bw_note = ""
 else:
 	die(1,"'%s': unrecognized invocation" % bn)
 
 opts_data = {
+# Can't use: share/Opts doesn't know anything about fmt codes
+#	'sets': [('hidden_incog_output_params',bool,'out_fmt','hi')],
 	'desc': desc.format(pnm=g.proj_name),
 	'usage': usage,
 	'options': """
 -h, --help            Print this help message.
 -d, --outdir=      d  Output files to directory 'd' instead of working dir.
+-e, --echo-passphrase Echo passphrases and other user input to screen.
 -i, --in-fmt=      f  {iaction} from wallet format 'f' (see FMT CODES below).
 -o, --out-fmt=     f  {oaction} to wallet format 'f' (see FMT CODES below).
 -H, --hidden-incog-input-params=f,o  Read hidden incognito data from file
@@ -78,20 +84,28 @@ opts_data = {
 -m, --keep-label      Reuse label of input wallet for output wallet.
 -p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
                       for password hashing (default: '{g.hash_preset}').
--P, --passwd-file= f  Get wallet passphrase from file 'f'
+-z, --show-hash-presets Show information on available hash presets.
+-P, --passwd-file= f  Get wallet passphrase from file 'f'.
 -q, --quiet           Produce quieter output; suppress some warnings.
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
                       (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}).
 -S, --stdout          Write wallet data to stdout instead of file.
 -v, --verbose         Produce more verbose output.
-
-FMT CODES:
-  {f}
 """.format(
 		g=g,
 		iaction=capfirst(iaction),
 		oaction=capfirst(oaction),
-		f="\n  ".join(SeedSource.format_fmt_codes().split("\n"))
+	),
+	'notes': """
+
+{pw_note}{bw_note}
+
+FMT CODES:
+  {f}
+""".format(
+	f="\n  ".join(SeedSource.format_fmt_codes().split("\n")),
+	pw_note=pw_note,
+	bw_note=("","\n\n" + bw_note)[int(bool(bw_note))]
 	)
 }
 

+ 0 - 167
mmgen/main_walletchk.py

@@ -1,167 +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-walletchk: Check integrity of an MMGen deterministic wallet, display
-                 information about it and export it to various formats
-"""
-
-import sys
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import *
-from mmgen.crypto import *
-
-opts_data = {
-	'desc':  """Check integrity of an {pnm} deterministic wallet, display
-                    its information, and export seed and mnemonic data.
-             """.format(pnm=g.proj_name),
-	'usage':   "[opts] [filename]",
-	'options': """
--h, --help             Print this help message
--d, --outdir=       d  Specify an alternate directory 'd' for output
--e, --echo-passphrase  Print passphrase to screen when typing it
--P, --passwd-file=  f  Get {pnm} wallet passphrase from file 'f'
--q, --quiet            Suppress warnings; overwrite files without prompting
--r, --usr-randchars= n Get 'n' characters of additional randomness from
-                       user (min={g.min_urandchars}, max={g.max_urandchars})
--S, --stdout           Print seed or mnemonic data to standard output
--v, --verbose          Produce more verbose output
--g, --export-incog     Export wallet to incognito format
--X, --export-incog-hex Export wallet to incognito hexadecimal format
--G, --export-incog-hidden=f,o  Hide incognito data in existing file 'f'
-                       at offset 'o' (comma-separated)
--o, --old-incog-fmt    Use old (pre-0.7.8) incog format
--m, --export-mnemonic  Export the wallet's mnemonic to file
--s, --export-seed      Export the wallet's seed to file
-""".format(g=g,pnm=g.proj_name),
-	'notes': """
-
-Since good randomness is particularly important for incognito wallets,
-the '--usr-randchars' option is turned on by default to gather additional
-entropy from the user when one of the '--export-incog*' options is
-selected.  If you fully trust your OS's random number generator and wish
-to disable this option, then specify '-r0' on the command line.
-"""
-}
-
-def wallet_to_incog_data(infile):
-
-	d = get_data_from_wallet(infile,silent=True)
-	seed_id,key_id,preset,salt,enc_seed = \
-			d[1][0], d[1][1], d[2].split(":")[0], d[3], d[4]
-
-	while True:
-		passwd = get_mmgen_passphrase("{pnm} wallet".format(pnm=g.proj_name))
-		key = make_key(passwd, salt, preset, "main key")
-		seed = decrypt_seed(enc_seed, key, seed_id, key_id)
-		if seed: break
-
-	iv = get_random(g.aesctr_iv_len)
-	iv_id = make_iv_chksum(iv)
-	msg("Incog ID: %s" % iv_id)
-
-	if not opt.old_incog_fmt:
-		salt = get_random(g.salt_len)
-		key = make_key(passwd, salt, preset, "incog wallet key")
-		key_id = make_chksum_8(key)
-		from hashlib import sha256
-		chk = sha256(seed).digest()[:8]
-		enc_seed = encrypt_data(chk+seed, key, 1, "seed")
-
-	# IV is used BOTH to initialize counter and to salt password!
-	key = make_key(passwd, iv, preset, "incog wrapper key")
-	wrap_enc = encrypt_data(salt+enc_seed,key,int(hexlify(iv),16),"incog data")
-
-	return iv+wrap_enc,seed_id,key_id,iv_id,preset
-
-
-def export_to_hidden_incog(incog_enc):
-	outfile,offset = opt.export_incog_hidden.split(",") #Already sanity-checked
-	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
-
-	dmsg("Incog data len %s, offset %s" % (len(incog_enc),offset))
-	check_data_fits_file_at_offset(outfile,int(offset),len(incog_enc),"write")
-
-	if not opt.quiet: confirm_or_exit("","alter file '%s'" % outfile)
-	import os
-	f = os.open(outfile,os.O_RDWR)
-	os.lseek(f, int(offset), os.SEEK_SET)
-	os.write(f, incog_enc)
-	os.close(f)
-	msg("Data written to file '%s' at offset %s" %
-			(os.path.relpath(outfile),offset))
-
-
-cmd_args = opt.opts.init(opts_data)
-
-if opt.export_incog_hidden or opt.export_incog_hex:
-	opt.export_incog = True
-
-if len(cmd_args) != 1: opt.opts.usage()
-
-check_infile(cmd_args[0])
-
-if opt.outdir and opt.export_incog_hidden:
-	msg("Warning: '--outdir' option is ignored when exporting hidden incog data")
-
-g.use_urandchars = True
-
-if opt.export_mnemonic:
-	qmsg("Exporting mnemonic data to file by user request")
-elif opt.export_seed:
-	qmsg("Exporting seed data to file by user request")
-elif opt.export_incog:
-	qmsg("Exporting wallet to incognito format by user request")
-	incog_enc,seed_id,key_id,iv_id,preset = \
-		wallet_to_incog_data(cmd_args[0])
-
-	if opt.export_incog_hidden:
-		export_to_hidden_incog(incog_enc)
-	else:
-		z = 0 if opt.old_incog_fmt else 8
-		seed_len = (len(incog_enc)-g.salt_len-g.aesctr_iv_len-z)*8
-		fn = "%s-%s-%s[%s,%s].%s" % (
-			seed_id, key_id, iv_id, seed_len, preset,
-			g.incog_hex_ext if opt.export_incog_hex else g.incog_ext
-		)
-		data = pretty_hexdump(incog_enc,2,8,line_nums=False) \
-					if opt.export_incog_hex else incog_enc
-		write_to_file_or_stdout(fn, data, "incognito wallet data")
-
-	sys.exit()
-
-seed = get_seed_retry(cmd_args[0])
-if seed: msg("Wallet is OK")
-else:
-	msg("Error opening wallet")
-	sys.exit(2)
-
-if opt.export_mnemonic:
-	wl = get_default_wordlist()
-	from mmgen.mnemonic import get_mnemonic_from_seed
-	mn = get_mnemonic_from_seed(seed, wl, g.default_wordlist, opt.debug)
-	fn = "%s.%s" % (make_chksum_8(seed).upper(), g.mn_ext)
-	write_to_file_or_stdout(fn, " ".join(mn)+"\n", "mnemonic data")
-
-elif opt.export_seed:
-	from mmgen.bitcoin import b58encode_pad
-	data = split_into_cols(4,b58encode_pad(seed))
-	chk = make_chksum_6(b58encode_pad(seed))
-	fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
-	write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), "seed data")

+ 0 - 172
mmgen/main_walletgen.py

@@ -1,172 +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-walletgen: Generate an MMGen deterministic wallet
-"""
-
-import sys, os
-from hashlib import sha256
-
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import *
-from mmgen.crypto import *
-
-pnm = g.proj_name
-
-opts_data = {
-	'desc':    "Generate an {pnm} deterministic wallet".format(pnm=pnm),
-	'usage':   "[opts] [infile]",
-	'options': """
--h, --help                 Print this help message
--d, --outdir=           d  Specify an alternate directory 'd' for output
--e, --echo-passphrase      Print passphrase to screen when typing it
--H, --show-hash-presets    Show information on available hash presets
--l, --seed-len=         n  Create seed of length 'n'. Options: {seed_lens}
-                           (default: {g.seed_len})
--L, --label=            l  Label to identify this wallet (32 chars max.
-                           Allowed symbols: A-Z, a-z, 0-9, " ", "_", ".")
--p, --hash-preset=      p  Use scrypt.hash() parameters from preset 'p'
-                           (default: '{g.hash_preset}')
--P, --passwd-file=      f  Get {pnm} wallet passphrase from file 'f'
--q, --quiet                Produce quieter output; overwrite files without
-                           prompting
--r, --usr-randchars=    n  Get 'n' characters of additional randomness from
-                           user (min={g.min_urandchars}, max={g.max_urandchars})
--v, --verbose              Produce more verbose output
-
--b, --from-brain=      l,p Generate wallet from a user-created passphrase,
-                           i.e. a "brainwallet", using seed length 'l' and
-                           hash preset 'p' (comma-separated)
--g, --from-incog           Generate wallet from an incognito-format wallet
--G, --from-incog-hidden=   f,o,l  Generate keys from incognito data in file
-                           'f' at offset 'o', with seed length of 'l'
--o, --old-incog-fmt        Use old (pre-0.7.8) incog format
--m, --from-mnemonic        Generate wallet from an Electrum-like mnemonic
--s, --from-seed            Generate wallet from a seed in .{g.seed_ext} format
-""".format(seed_lens=",".join([str(i) for i in g.seed_lens]),g=g,pnm=pnm),
-	'notes': """
-
-By default (i.e. when invoked without any of the '--from-<what>' options),
-{g.prog_name} generates a wallet based on a random seed.
-
-Data for the --from-<what> options will be taken from <infile> if <infile>
-is specified.  Otherwise, the user will be prompted to enter the data.
-
-For passphrases all combinations of whitespace are equal, and leading and
-trailing space are ignored.  This permits reading passphrase data from a
-multi-line file with free spacing and indentation.  This is particularly
-convenient for long brainwallet passphrases, for example.
-
-Since good randomness is particularly important when generating wallets,
-the '--usr-randchars' option is turned on by default to gather additional
-entropy from the user.  If you fully trust your OS's random number gener-
-ator and wish to disable this option, specify '-r0' on the command line.
-
-BRAINWALLET NOTE:
-
-As brainwallets require especially strong hashing to thwart dictionary
-attacks, the brainwallet hash preset must be specified by the user, using
-the 'p' parameter of the '--from-brain' option.  This preset should be
-stronger than the one used for hashing the seed (i.e. the default value or
-the one specified in the '--hash-preset' option).
-
-The '--from-brain' option also requires the user to specify a seed length
-(the 'l' parameter), which overrides both the default and any one given in
-the '--seed-len' option.
-
-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(g=g)
-}
-
-wmsg = {
-	'choose_wallet_passphrase': """
-You must choose a passphrase to encrypt the wallet with.  A key will be
-generated from your passphrase using a hash preset of '%s'.  Please note that
-no strength checking of passphrases is performed.  For an empty passphrase,
-just hit ENTER twice.
-""".strip(),
-	'brain_warning': """
-############################## EXPERTS ONLY! ##############################
-
-A brainwallet will be secure only if you really know what you're doing and
-have put much care into its creation.  The creators of {pnm} assume no
-responsibility for coins stolen as a result of a poorly crafted brainwallet
-passphrase.
-
-A key will be generated from your passphrase using the parameters requested
-by you: seed length {}, hash preset '{}'.  For brainwallets it's highly
-recommended to use one of the higher-numbered presets.
-
-Remember the seed length and hash preset parameters you've specified.  To
-generate the correct keys/addresses associated with this passphrase in the
-future, you must continue using these same parameters.
-""",
-}
-
-import mmgen.opt as opt
-cmd_args = opt.opts.init(opts_data)
-
-if len(cmd_args) == 1:
-	infile = cmd_args[0]
-	check_infile(infile)
-	ext = infile.split(".")[-1]
-	ok_exts = g.seedfile_exts
-	for e in ok_exts:
-		if e == ext: break
-	else:
-		msg(
-"Input file must have one of the following extensions: .%s" % ", .".join(ok_exts))
-		sys.exit(1)
-elif len(cmd_args) == 0:
-	infile = ""
-else: opt.opts.usage()
-
-g.use_urandchars = True
-
-# Begin execution
-
-do_license_msg()
-
-if opt.from_brain and not opt.quiet:
-	confirm_or_exit(wmsg['brain_warning'].format(
-			pnm=pnm, *get_from_brain_opt_params()),
-		"continue")
-
-if infile or any([
-		opt.from_mnemonic,opt.from_brain,opt.from_seed,opt.from_incog]):
-	seed = get_seed_retry(infile)
-	qmsg("")
-else:
-	# Truncate random data for smaller seed lengths
-	seed = sha256(get_random(128)).digest()[:opt.seed_len/8]
-
-salt = sha256(get_random(128)).digest()[:g.salt_len]
-
-qmsg(wmsg['choose_wallet_passphrase'] % opt.hash_preset)
-
-passwd = get_new_passphrase("new {pnm} wallet".format(pnm=pnm))
-
-key = make_key(passwd, salt, opt.hash_preset)
-
-enc_seed = encrypt_seed(seed, key)
-
-write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed)

+ 42 - 41
mmgen/opts.py

@@ -26,6 +26,20 @@ import mmgen.share.Opts
 import opt
 from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die,is_mmgen_wallet_label
 
+pw_note = """
+For passphrases all combinations of whitespace are equal and leading and
+trailing space is ignored.  This permits reading passphrase or brainwallet
+data from a multi-line file with free spacing and indentation.
+""".strip()
+
+bw_note = """
+BRAINWALLET NOTE:
+
+To thwart dictionary attacks, it's recommended to use a strong hash preset
+with brainwallets.  For a brainwallet passphrase to generate the correct
+seed, the same seed length and hash preset parameters must always be used.
+""".strip()
+
 def usage():
 	Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
 	sys.exit(2)
@@ -175,9 +189,10 @@ def check_opts(usr_opts):       # Returns false if any check fails
 					(val,desc,n,sepword))
 			return False
 
-	def opt_compares(val,op,target,desc):
+	def opt_compares(val,op,target,desc,what=""):
+		if what: what += " "
 		if not eval("%s %s %s" % (val, op, target)):
-			msg("%s: invalid %s (not %s %s)" % (val,desc,op,target))
+			msg("%s: invalid %s (%snot %s %s)" % (val,desc,what,op,target))
 			return False
 		return True
 
@@ -212,15 +227,14 @@ def check_opts(usr_opts):       # Returns false if any check fails
 
 		desc = "parameter for '%s' option" % fmt_opt(key)
 
+		from mmgen.util import check_infile,check_outfile,check_outdir
 		# Check for file existence and readability
-		from mmgen.util import check_infile
 		if key in ('keys_from_file','mmgen_keys_from_file',
 				'passwd_file','keysforaddrs','comment_file'):
 			check_infile(val)  # exits on error
 			continue
 
 		if key == 'outdir':
-			from mmgen.util import check_outdir
 			check_outdir(val)  # exits on error
 		elif key == 'label':
 			if not is_mmgen_wallet_label(val):
@@ -244,10 +258,22 @@ def check_opts(usr_opts):       # Returns false if any check fails
 				elif issubclass(sstype,Brainwallet):
 					die(1,"Output to brainwallet format unsupported")
 		elif key in ('hidden_incog_input_params','hidden_incog_output_params'):
+			a = val.split(",")
+			if len(a) != 2:
+				opt_display(key,val)
+				msg("Option requires two comma-separated arguments")
+				return False
+			if not opt_is_int(a[1],desc): return False
 			if key == 'hidden_incog_input_params':
-				check_infile(val.split(",")[0])
+				check_infile(a[0],blkdev_ok=True)
 				key2 = 'in_fmt'
 			else:
+				import os
+				try: os.stat(a[0])
+				except:
+					b = os.path.dirname(a[0])
+					if b: check_outdir(b)
+				else: check_outfile(a[0],blkdev_ok=True)
 				key2 = 'out_fmt'
 			if hasattr(opt,key2):
 				val2 = getattr(opt,key2)
@@ -257,47 +283,22 @@ def check_opts(usr_opts):       # Returns false if any check fails
 						"Option conflict:\n  %s, with\n  %s=%s" % (
 						fmt_opt(key),fmt_opt(key2),val2
 					))
-
-		# begin OLD, deprecated
-		elif key == 'hidden_incog_params':
-			from mmgen.util import check_outfile
-			if not opt_splits(val,",",2,desc): return False
-			outfile,offset = val.split(",")
-			check_outfile(outfile)
-			w = "offset " + desc
-			if not opt_is_int(offset,w): return False
-			if not opt_compares(offset,">=",0,desc): return False
-		elif key == 'export_incog_hidden' or key == 'from_incog_hidden':
-			if key == 'from_incog_hidden':
-				if not opt_splits(val,",",3,desc): return False
-				infile,offset,seed_len = val.split(",")
-				from mmgen.util import check_infile
-				check_infile(infile)
-				w = "seed length " + desc
-				if not opt_is_int(seed_len,w): return False
-				if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False
-			else:
-				from mmgen.util import check_outfile
-				if not opt_splits(val,",",2,desc): return False
-				outfile,offset = val.split(",")
-				check_outfile(outfile)
-			w = "offset " + desc
-			if not opt_is_int(offset,w): return False
-			if not opt_compares(offset,">=",0,desc): return False
-		elif key == 'from_brain':
-			if not opt_splits(val,",",2,desc): return False
-			l,p = val.split(",")
-			w = "seed length " + desc
-			if not opt_is_int(l,w): return False
-			if not opt_is_in_list(int(l),g.seed_lens,w): return False
-			w = "hash preset " + desc
-			if not opt_is_in_list(p,g.hash_presets.keys(),w): return False
-		# end OLD
 		elif key == 'seed_len':
 			if not opt_is_int(val,desc): return False
 			if not opt_is_in_list(int(val),g.seed_lens,desc): return False
 		elif key == 'hash_preset':
 			if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False
+		elif key == 'brain_params':
+			a = val.split(",")
+			if len(a) != 2:
+				opt_display(key,val)
+				msg("Option requires two comma-separated arguments")
+				return False
+			d = "seed length " + desc
+			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
+			d = "hash preset " + desc
+			if not opt_is_in_list(a[1],g.hash_presets.keys(),d): return False
 		elif key == 'usr_randchars':
 			if val == 0: continue
 			if not opt_is_int(val,desc): return False

+ 21 - 12
mmgen/seed.py

@@ -64,7 +64,7 @@ class SeedSource(MMGenObject):
 
 	class SeedSourceData(MMGenObject): pass
 
-	def __new__(cls,fn=None,ss=None,ignore_in_fmt_opt=False,passchg=False):
+	def __new__(cls,fn=None,ss=None,ignore_in_fmt=False,passchg=False):
 
 		def die_on_opt_mismatch(opt,sstype):
 			opt_sstype = cls.fmt_code_to_sstype(opt)
@@ -91,7 +91,7 @@ class SeedSource(MMGenObject):
 				f  = Filename(fn,ftype="hincog")
 				sstype = cls.fmt_code_to_sstype("hincog")
 
-			if opt.in_fmt and not ignore_in_fmt_opt:
+			if opt.in_fmt and not ignore_in_fmt:
 				die_on_opt_mismatch(opt.in_fmt,sstype)
 
 			me = super(cls,cls).__new__(sstype)
@@ -109,7 +109,7 @@ class SeedSource(MMGenObject):
 
 		return me
 
-	def __init__(self,fn=None,ss=None,ignore_in_fmt_opt=False,passchg=False):
+	def __init__(self,fn=None,ss=None,ignore_in_fmt=False,passchg=False):
 
 		self.ssdata = self.SeedSourceData()
 		self.msg = {}
@@ -692,16 +692,26 @@ class Brainwallet (SeedSourceEnc):
 	ext = "mmbrain"
 	# brainwallet warning message? TODO
 
+	def get_bw_params(self):
+		# already checked
+		a = opt.brain_params.split(",")
+		return int(a[0]),a[1]
+
 	def _deformat(self):
 		self.brainpasswd = " ".join(self.fmt_data.split())
 		return True
 
 	def _decrypt(self):
-		self._get_hash_preset()
+		d = self.ssdata
+		if hasattr(opt,"brain_params"):
+			seed_len,d.hash_preset = self.get_bw_params()
+		else:
+			self._get_hash_preset()
+			seed_len = opt.seed_len
 		vmsg_r("Hashing brainwallet data.  Please wait...")
 		# Use buflen arg of scrypt.hash() to get seed of desired length
 		seed = scrypt_hash_passphrase(self.brainpasswd, "",
-					self.ssdata.hash_preset, buflen=opt.seed_len/8)
+					d.hash_preset, buflen=seed_len/8)
 		vmsg("Done")
 		self.seed = Seed(seed)
 		msg("Seed ID: %s" % self.seed.sid)
@@ -921,8 +931,8 @@ harder to find, you're advised to choose a much larger file size than this.
 		m = ("Input","Destination")[int(action=="write")]
 		if fn.size < d.hincog_offset + d.target_data_len:
 			die(1,
-	"%s file has length %s, too short to %s %s bytes of data at offset %s"
-				% (m,fn.size,action,d.target_data_len,d.hincog_offset))
+	"%s file '%s' has length %s, too short to %s %s bytes of data at offset %s"
+				% (m,fn.name,fn.size,action,d.target_data_len,d.hincog_offset))
 
 	def _get_data(self):
 		d = self.ssdata
@@ -941,7 +951,6 @@ harder to find, you're advised to choose a much larger file size than this.
 		qmsg("Data read from file '%s' at offset %s" %
 				(self.infile.name,d.hincog_offset), "Data read from file")
 
-
 	# overrides method in SeedSource
 	def write_to_file(self):
 		d = self.ssdata
@@ -972,9 +981,10 @@ harder to find, you're advised to choose a much larger file size than this.
 			else:
 				die(1,"Exiting at user request")
 
-		self.outfile = f = Filename(fn,ftype="hincog")
+		self.outfile = f = Filename(fn,ftype=self.fmt_codes[0],write=True)
 
-		dmsg("Incog data len %s, offset %s" % (d.target_data_len,d.hincog_offset))
+		dmsg("%s data len %s, offset %s" % (
+				capfirst(self.desc),d.target_data_len,d.hincog_offset))
 
 		if check_offset:
 			self._check_valid_offset(f,"write")
@@ -985,5 +995,4 @@ harder to find, you're advised to choose a much larger file size than this.
 		os.write(fh, self.fmt_data)
 		os.close(fh)
 		msg("%s written to file '%s' at offset %s" % (
-				capfirst(self.desc),
-				f.name,d.hincog_offset))
+				capfirst(self.desc),f.name,d.hincog_offset))

+ 1 - 1
mmgen/share/Opts.py

@@ -39,7 +39,7 @@ def process_opts(argv,opts_data,short_opts,long_opts):
 	opts_data['prog_name'] = os.path.basename(sys.argv[0])
 	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:
 		print str(err); sys.exit(2)
 

+ 23 - 16
mmgen/util.py

@@ -132,11 +132,11 @@ def suf(arg,suf_type):
 def get_extension(f):
 	return os.path.splitext(f)[1][1:]
 
-def make_chksum_N(s,n,sep=False):
-	if n%4 or not (4 <= n <= 64): return False
+def make_chksum_N(s,nchars,sep=False):
+	if nchars%4 or not (4 <= nchars <= 64): return False
 	s = sha256(sha256(s).digest()).hexdigest().upper()
 	sep = " " if sep else ""
-	return sep.join([s[i*4:i*4+4] for i in range(n/4)])
+	return sep.join([s[i*4:i*4+4] for i in range(nchars/4)])
 
 def make_chksum_8(s,sep=False):
 	s = sha256(sha256(s).digest()).hexdigest()[:8].upper()
@@ -273,36 +273,43 @@ def open_file_or_exit(filename,mode):
 	return f
 
 
-def check_file_type_and_access(fname,ftype):
+def check_file_type_and_access(fname,ftype,blkdev_ok=False):
 
 	import os, stat
 
-	typ2,tdesc2,access,action  = (stat.S_ISLNK,"symbolic link",os.R_OK,"read")\
-	if ftype == "input file" else (stat.S_ISBLK,"block device",os.W_OK,"writ")
+	a = ((os.R_OK,"read"),(os.W_OK,"writ"))
+	access,m = a[int(ftype in ("output file","output directory"))]
 
-	if ftype == "directory":
-		typ1,typ2,tdesc = stat.S_ISDIR,stat.S_ISDIR,"directory"
-	else:
-		typ1,tdesc = stat.S_ISREG,"regular file or "+tdesc2
+	ok_types = [
+		(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")]
 
 	try: mode = os.stat(fname).st_mode
 	except:
 		msg("Unable to stat requested %s '%s'" % (ftype,fname))
 		sys.exit(1)
 
-	if not (typ1(mode) or typ2(mode)):
-		msg("Requested %s '%s' is not a %s" % (ftype,fname,tdesc))
+	for t in ok_types:
+		if t[0](mode): break
+	else:
+		msg("Requested %s '%s' is not a %s" % (ftype,fname,
+				" or ".join([t[1] for t in ok_types])))
 		sys.exit(1)
 
 	if not os.access(fname, access):
-		msg("Requested %s '%s' is un%sable by you" % (ftype,fname,action))
+		msg("Requested %s '%s' is not %sable by you" % (ftype,fname,m))
 		sys.exit(1)
 
 	return True
 
-def check_infile(f):  return check_file_type_and_access(f,"input file")
-def check_outfile(f): return check_file_type_and_access(f,"output file")
-def check_outdir(f):  return check_file_type_and_access(f,"directory")
+def check_infile(f,blkdev_ok=False):
+	return check_file_type_and_access(f,"input file",blkdev_ok=blkdev_ok)
+def check_outfile(f,blkdev_ok=False):
+	return check_file_type_and_access(f,"output file",blkdev_ok=blkdev_ok)
+def check_outdir(f):  return check_file_type_and_access(f,"output directory")
 
 def _validate_addr_num(n):
 

+ 1 - 4
setup.py

@@ -21,7 +21,7 @@ from distutils.core import setup
 setup(
 		name         = 'mmgen',
 		description   = 'A complete Bitcoin cold-storage solution for the command line',
-		version      = '0.8.0',
+		version      = '0.8.1rc1',
 		author       = 'Philemon',
 		author_email = 'mmgen-py@yandex.com',
 		url          = 'https://github.com/mmgen/mmgen',
@@ -52,15 +52,12 @@ setup(
 			'mmgen.main',
 			'mmgen.main_addrgen',
 			'mmgen.main_addrimport',
-			'mmgen.main_passchg',
 			'mmgen.main_pywallet',
 			'mmgen.main_tool',
 			'mmgen.main_txcreate',
 			'mmgen.main_txsend',
 			'mmgen.main_txsign',
 			'mmgen.main_wallet',
-			'mmgen.main_walletchk',
-			'mmgen.main_walletgen',
 
 			'mmgen.share.__init__',
 			'mmgen.share.Opts',

+ 0 - 0
test/ref/brainwallet-spaced → test/ref/wallet-spaced.mmbrain


+ 0 - 0
test/ref/brainwallet → test/ref/wallet.mmbrain


+ 0 - 1535
test/test-oldscripts.py

@@ -1,1535 +0,0 @@
-#!/usr/bin/python
-
-# 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
-pn = os.path.dirname(sys.argv[0])
-os.chdir(os.path.join(pn,os.pardir))
-sys.path.__setitem__(0,os.path.abspath(os.curdir))
-
-import mmgen.globalvars as g
-import mmgen.opt as opt
-from mmgen.util import mmsg,mdie,Msg,die,capfirst
-from mmgen.test import *
-
-hincog_fn      = "rand_data"
-hincog_bytes   = 1024*1024
-hincog_offset  = 98765
-hincog_seedlen = 256
-
-incog_id_fn  = "incog_id"
-non_mmgen_fn = "btckey"
-
-ref_dir = os.path.join("test","ref")
-
-ref_wallet_brainpass = "abc"
-ref_wallet_hash_preset = "1"
-ref_wallet_incog_offset = 123
-
-ref_bw_hash_preset = "1"
-ref_bw_file        = "brainwallet"
-ref_bw_file_spc    = "brainwallet-spaced"
-
-ref_kafile_pass        = "kafile password"
-ref_kafile_hash_preset = "1"
-
-ref_enc_fn = "sample-text.mmenc"
-tool_enc_passwd = "Scrypt it, don't hash it!"
-sample_text = \
-	"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n"
-
-cfgs = {
-	'6': {
-		'name':            "reference wallet check (128-bit)",
-		'seed_len':        128,
-		'seed_id':         "FE3C6545",
-		'ref_bw_seed_id':  "33F10310",
-		'addrfile_chk':    "B230 7526 638F 38CB 8FDC 8B76",
-		'keyaddrfile_chk': "CF83 32FB 8A8B 08E2 0F00 D601",
-		'wpasswd':         "reference password",
-		'ref_wallet':      "FE3C6545-D782B529[128,1].mmdat",
-		'ic_wallet':       "FE3C6545-E29303EA-5E229E30[128,1].mmincog",
-		'ic_wallet_hex':   "FE3C6545-BC4BE3F2-32586837[128,1].mmincox",
-
-		'hic_wallet':       "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
-		'hic_wallet_old':   "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
-
-		'tmpdir':        os.path.join("test","tmp6"),
-		'kapasswd':      "",
-		'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
-		'dep_generators':  {
-			'mmdat':       "refwalletgen1",
-			'addrs':       "refaddrgen1",
-			'akeys.mmenc': "refkeyaddrgen1"
-		},
-
-	},
-	'7': {
-		'name':            "reference wallet check (192-bit)",
-		'seed_len':        192,
-		'seed_id':         "1378FC64",
-		'ref_bw_seed_id':  "CE918388",
-		'addrfile_chk':    "8C17 A5FA 0470 6E89 3A87 8182",
-		'keyaddrfile_chk': "9648 5132 B98E 3AD9 6FC3 C5AD",
-		'wpasswd':         "reference password",
-		'ref_wallet':      "1378FC64-6F0F9BB4[192,1].mmdat",
-		'ic_wallet':       "1378FC64-2907DE97-F980D21F[192,1].mmincog",
-		'ic_wallet_hex':   "1378FC64-4DCB5174-872806A7[192,1].mmincox",
-
-		'hic_wallet':       "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
-		'hic_wallet_old':   "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
-
-		'tmpdir':        os.path.join("test","tmp7"),
-		'kapasswd':      "",
-		'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
-		'dep_generators':  {
-			'mmdat':       "refwalletgen2",
-			'addrs':       "refaddrgen2",
-			'akeys.mmenc': "refkeyaddrgen2"
-		},
-
-	},
-	'8': {
-		'name':            "reference wallet check (256-bit)",
-		'seed_len':        256,
-		'seed_id':         "98831F3A",
-		'ref_bw_seed_id':  "B48CD7FC",
-		'addrfile_chk':    "6FEF 6FB9 7B13 5D91 854A 0BD3",
-		'keyaddrfile_chk': "9F2D D781 1812 8BAD C396 9DEB",
-		'wpasswd':         "reference password",
-		'ref_wallet':      "98831F3A-27F2BF93[256,1].mmdat",
-		'ref_addrfile':    "98831F3A[1,31-33,500-501,1010-1011].addrs",
-		'ref_keyaddrfile': "98831F3A[1,31-33,500-501,1010-1011].akeys.mmenc",
-		'ref_addrfile_chksum':    "6FEF 6FB9 7B13 5D91 854A 0BD3",
-		'ref_keyaddrfile_chksum': "9F2D D781 1812 8BAD C396 9DEB",
-
-#		'ref_fake_unspent_data':"98831F3A_unspent.json",
-		'ref_tx_file':     "tx_FFB367[1.234].raw",
-		'ic_wallet':       "98831F3A-5482381C-18460FB1[256,1].mmincog",
-		'ic_wallet_hex':   "98831F3A-1630A9F2-870376A9[256,1].mmincox",
-
-		'hic_wallet':       "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
-		'hic_wallet_old':   "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
-
-		'tmpdir':        os.path.join("test","tmp8"),
-		'kapasswd':      "",
-		'addr_idx_list': "1010,500-501,31-33,1,33,500,1011", # 8 addresses
-		'dep_generators':  {
-			'mmdat':       "refwalletgen3",
-			'addrs':       "refaddrgen3",
-			'akeys.mmenc': "refkeyaddrgen3"
-		},
-	},
-	'1': {
-		'tmpdir':        os.path.join("test","tmp1"),
-		'wpasswd':       "Dorian",
-		'kapasswd':      "Grok the blockchain",
-		'addr_idx_list': "12,99,5-10,5,12", # 8 addresses
-		'dep_generators':  {
-			'mmdat':       "walletgen",
-			'addrs':       "addrgen",
-			'raw':         "txcreate",
-			'sig':         "txsign",
-			'mmwords':     "export_mnemonic",
-			'mmseed':      "export_seed",
-			'mmincog':     "export_incog",
-			'mmincox':     "export_incog_hex",
-			hincog_fn:     "export_incog_hidden",
-			incog_id_fn:   "export_incog_hidden",
-			'akeys.mmenc': "keyaddrgen"
-		},
-	},
-	'2': {
-		'tmpdir':        os.path.join("test","tmp2"),
-		'wpasswd':       "Hodling away",
-		'addr_idx_list': "37,45,3-6,22-23",  # 8 addresses
-		'seed_len':      128,
-		'dep_generators': {
-			'mmdat':       "walletgen2",
-			'addrs':       "addrgen2",
-			'raw':         "txcreate2",
-			'sig':         "txsign2",
-			'mmwords':     "export_mnemonic2",
-		},
-	},
-	'3': {
-		'tmpdir':        os.path.join("test","tmp3"),
-		'wpasswd':       "Major miner",
-		'addr_idx_list': "73,54,1022-1023,2-5", # 8 addresses
-		'dep_generators': {
-			'mmdat':       "walletgen3",
-			'addrs':       "addrgen3",
-			'raw':         "txcreate3",
-			'sig':         "txsign3"
-		},
-	},
-	'4': {
-		'tmpdir':        os.path.join("test","tmp4"),
-		'wpasswd':       "Hashrate rising",
-		'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses
-		'seed_len':      192,
-		'dep_generators': {
-			'mmdat':       "walletgen4",
-			'mmbrain':     "walletgen4",
-			'addrs':       "addrgen4",
-			'raw':         "txcreate4",
-			'sig':         "txsign4",
-		},
-		'bw_filename': "brainwallet.mmbrain",
-		'bw_params':   "192,1",
-	},
-	'5': {
-		'tmpdir':        os.path.join("test","tmp5"),
-		'wpasswd':       "My changed password",
-		'dep_generators': {
-			'mmdat':       "passchg",
-		},
-	},
-	'9': {
-		'tmpdir':        os.path.join("test","tmp9"),
-		'tool_enc_infn':      "tool_encrypt.in",
-#		'tool_enc_ref_infn':  "tool_encrypt_ref.in",
-		'dep_generators': {
-			'tool_encrypt.in':            "tool_encrypt",
-			'tool_encrypt.in.mmenc':      "tool_encrypt",
-#			'tool_encrypt_ref.in':        "tool_encrypt_ref",
-#			'tool_encrypt_ref.in.mmenc':  "tool_encrypt_ref",
-		},
-	},
-}
-
-from copy import deepcopy
-for a,b in ('6','11'),('7','12'),('8','13'):
-	cfgs[b] = deepcopy(cfgs[a])
-	cfgs[b]['tmpdir'] = os.path.join("test","tmp"+b)
-
-from collections import OrderedDict
-cmd_data = OrderedDict([
-#     test               description                  depends
-	['walletgen',       (1,'wallet generation',        [[[],1]])],
-#	['walletchk',       (1,'wallet check',             [[["mmdat"],1]])],
-	['passchg',         (5,'password, label and hash preset change',[[["mmdat"],1]])],
-	['walletchk_newpass',(5,'wallet check with new pw, label and hash preset',[[["mmdat"],5]])],
-	['addrgen',         (1,'address generation',       [[["mmdat"],1]])],
-	['addrimport',      (1,'address import',           [[["addrs"],1]])],
-	['txcreate',        (1,'transaction creation',     [[["addrs"],1]])],
-	['txsign',          (1,'transaction signing',      [[["mmdat","raw"],1]])],
-	['txsend',          (1,'transaction sending',      [[["sig"],1]])],
-
-	['export_seed',     (1,'seed export to mmseed format',   [[["mmdat"],1]])],
-	['export_mnemonic', (1,'seed export to mmwords format',  [[["mmdat"],1]])],
-	['export_incog',    (1,'seed export to mmincog format',  [[["mmdat"],1]])],
-	['export_incog_hex',(1,'seed export to mmincog hex format', [[["mmdat"],1]])],
-	['export_incog_hidden',(1,'seed export to hidden mmincog format', [[["mmdat"],1]])],
-
-	['addrgen_seed',    (1,'address generation from mmseed file', [[["mmseed","addrs"],1]])],
-	['addrgen_mnemonic',(1,'address generation from mmwords file',[[["mmwords","addrs"],1]])],
-	['addrgen_incog',   (1,'address generation from mmincog file',[[["mmincog","addrs"],1]])],
-	['addrgen_incog_hex',(1,'address generation from mmincog hex file',[[["mmincox","addrs"],1]])],
-	['addrgen_incog_hidden',(1,'address generation from hidden mmincog file', [[[hincog_fn,"addrs"],1]])],
-
-	['keyaddrgen',    (1,'key-address file generation', [[["mmdat"],1]])],
-	['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
-
-	['walletgen2',(2,'wallet generation (2), 128-bit seed',     [])],
-	['addrgen2',  (2,'address generation (2)',    [[["mmdat"],2]])],
-	['txcreate2', (2,'transaction creation (2)',  [[["addrs"],2]])],
-	['txsign2',   (2,'transaction signing, two transactions',[[["mmdat","raw"],1],[["mmdat","raw"],2]])],
-	['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
-
-	['walletgen3',(3,'wallet generation (3)',                  [])],
-	['addrgen3',  (3,'address generation (3)',                 [[["mmdat"],3]])],
-	['txcreate3', (3,'tx creation with inputs and outputs from two wallets', [[["addrs"],1],[["addrs"],3]])],
-	['txsign3',   (3,'tx signing with inputs and outputs from two wallets',[[["mmdat"],1],[["mmdat","raw"],3]])],
-
-	['walletgen4',(4,'wallet generation (4) (brainwallet)',    [])],
-	['addrgen4',  (4,'address generation (4)',                 [[["mmdat"],4]])],
-	['txcreate4', (4,'tx creation with inputs and outputs from four seed sources, plus non-MMGen inputs and outputs', [[["addrs"],1],[["addrs"],2],[["addrs"],3],[["addrs"],4]])],
-	['txsign4',   (4,'tx signing with inputs and outputs from incog file, mnemonic file, wallet and brainwallet, plus non-MMGen inputs and outputs', [[["mmincog"],1],[["mmwords"],2],[["mmdat"],3],[["mmbrain","raw"],4]])],
-	['tool_encrypt',     (9,"'mmgen-tool encrypt' (random data)",     [])],
-	['tool_decrypt',     (9,"'mmgen-tool decrypt' (random data)", [[[cfgs['9']['tool_enc_infn'],cfgs['9']['tool_enc_infn']+".mmenc"],9]])],
-#	['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)",  [])],
-	['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])],
-])
-
-# saved reference data
-cmd_data_ref = (
-	# reading
-	('ref_wallet_chk', ([],'saved reference wallet')),
-	('ref_seed_chk',   ([],'saved seed file')),
-	('ref_mn_chk',     ([],'saved mnemonic file')),
-	('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
-	('ref_brain_chk',  ([],'saved brainwallet')),
-	# generating new reference ('abc' brainwallet) files:
-	('refwalletgen',   ([],'gen new refwallet')),
-	('refaddrgen',     (["mmdat"],'new refwallet addr chksum')),
-	('refkeyaddrgen',  (["mmdat"],'new refwallet key-addr chksum'))
-)
-
-# misc. saved reference data
-cmd_data_ref_other = (
-	('ref_addrfile_chk',   'saved reference address file'),
-	('ref_keyaddrfile_chk','saved reference key-address file'),
-#	Create the fake inputs:
-#	('txcreate8',          'transaction creation (8)'),
-	('ref_tx_chk',         'saved reference tx file'),
-	('ref_brain_chk_spc3', 'saved brainwallet (non-standard spacing)'),
-	('ref_tool_decrypt',   'decryption of saved MMGen-encrypted file'),
-)
-
-# mmgen-walletconv:
-cmd_data_conv_in = ( # reading
-	('ref_wallet_conv',    'conversion of saved reference wallet'),
-	('ref_mn_conv',        'conversion of saved mnemonic'),
-	('ref_seed_conv',      'conversion of saved seed file'),
-	('ref_brain_conv',     'conversion of ref brainwallet'),
-	('ref_incog_conv',     'conversion of saved incog wallet'),
-	('ref_incox_conv',     'conversion of saved hex incog wallet'),
-	('ref_hincog_conv',    'conversion of saved hidden incog wallet'),
-	('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)')
-)
-cmd_data_conv_out = ( # writing
-	('ref_wallet_conv_out', 'ref seed conversion to wallet'),
-	('ref_mn_conv_out',     'ref seed conversion to mnemonic'),
-	('ref_seed_conv_out',   'ref seed conversion to seed'),
-	('ref_incog_conv_out',  'ref seed conversion to incog data'),
-	('ref_incox_conv_out',  'ref seed conversion to hex incog data'),
-	('ref_hincog_conv_out', 'ref seed conversion to hidden incog data')
-)
-
-cmd_groups = OrderedDict([
-	('main',      cmd_data.keys()),
-	('ref',       [c[0]+str(i) for c in cmd_data_ref for i in (1,2,3)]),
-	('ref_other', [c[0] for c in cmd_data_ref_other]),
-	('conv_in',   [c[0]+str(i) for c in cmd_data_conv_in for i in (1,2,3)]),
-	('conv_out',  [c[0]+str(i) for c in cmd_data_conv_out for i in (1,2,3)]),
-])
-
-for a,b in cmd_data_ref:
-	for i,j in (1,128),(2,192),(3,256):
-		cmd_data[a+str(i)] = (5+i,"%s (%s-bit)" % (b[1],j),[[b[0],5+i]])
-
-for a,b in cmd_data_ref_other:
-	cmd_data[a] = (8,b,[[[],8]])
-
-for a,b in cmd_data_conv_in:
-	for i,j in (1,128),(2,192),(3,256):
-		cmd_data[a+str(i)] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]])
-
-for a,b in cmd_data_conv_out:
-	for i,j in (1,128),(2,192),(3,256):
-		cmd_data[a+str(i)] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]])
-
-utils = {
-	'check_deps': 'check dependencies for specified command',
-	'clean':      'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
-}
-
-addrs_per_wallet = 8
-
-# total of two outputs must be < 10 BTC
-for k in cfgs.keys():
-	cfgs[k]['amts'] = [0,0]
-	for idx,mod in (0,6),(1,4):
-		cfgs[k]['amts'][idx] = "%s.%s" % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
-
-meta_cmds = OrderedDict([
-	['ref1', ("refwalletgen1","refaddrgen1","refkeyaddrgen1")],
-	['ref2', ("refwalletgen2","refaddrgen2","refkeyaddrgen2")],
-	['ref3', ("refwalletgen3","refaddrgen3","refkeyaddrgen3")],
-	['gen',  ("walletgen","addrgen")],
-	['pass', ("passchg","walletchk_newpass")],
-	['tx',   ("addrimport","txcreate","txsign","txsend")],
-	['export', [k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1]],
-	['gen_sp', [k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1]],
-	['online', ("keyaddrgen","txsign_keyaddr")],
-	['2', [k for k in cmd_data if cmd_data[k][0] == 2]],
-	['3', [k for k in cmd_data if cmd_data[k][0] == 3]],
-	['4', [k for k in cmd_data if cmd_data[k][0] == 4]],
-
-	['tool', ("tool_encrypt","tool_decrypt","tool_find_incog_data")],
-
-	['saved_ref1', [c[0]+"1" for c in cmd_data_ref]],
-	['saved_ref2', [c[0]+"2" for c in cmd_data_ref]],
-	['saved_ref3', [c[0]+"3" for c in cmd_data_ref]],
-
-	['saved_ref_other', [c[0] for c in cmd_data_ref_other]],
-
-	['saved_ref_conv_in1', [c[0]+"1" for c in cmd_data_conv_in]],
-	['saved_ref_conv_in2', [c[0]+"2" for c in cmd_data_conv_in]],
-	['saved_ref_conv_in3', [c[0]+"3" for c in cmd_data_conv_in]],
-
-	['saved_ref_conv_out1', [c[0]+"1" for c in cmd_data_conv_out]],
-	['saved_ref_conv_out2', [c[0]+"2" for c in cmd_data_conv_out]],
-	['saved_ref_conv_out3', [c[0]+"3" for c in cmd_data_conv_out]],
-])
-
-opts_data = {
-	'desc': "Test suite for the MMGen suite",
-	'usage':"[options] [command(s) or metacommand(s)]",
-	'options': """
--h, --help          Print this help message
--b, --buf-keypress  Use buffered keypresses as with real human input
--d, --debug-scripts Turn on debugging output in executed scripts
--D, --direct-exec   Bypass pexpect and execute a command directly (for
-                    debugging only)
--e, --exact-output  Show the exact output of the MMGen script(s) being run
--l, --list-cmds     List and describe the tests and commands in the test suite
--n, --names         Display command names instead of descriptions
--p, --pause         Pause between tests, resuming on keypress
--q, --quiet         Produce minimal output.  Suppress dependency info
--s, --system        Test scripts and modules installed on system rather than
-                    those in the repo root
--v, --verbose       Produce more verbose output
-""",
-	'notes': """
-
-If no command is given, the whole suite of tests is run.
-"""
-}
-
-cmd_args = opt.opts.init(opts_data)
-
-if opt.system: sys.path.pop(0)
-
-# temporary
-os.environ["MMGEN_USE_OLD_SCRIPTS"] = "1"
-
-if opt.debug_scripts: os.environ["MMGEN_DEBUG"] = "1"
-
-if opt.buf_keypress:
-	send_delay = 0.3
-else:
-	send_delay = 0
-	os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
-
-if opt.exact_output:
-	def msg(s): pass
-	vmsg = vmsg_r = msg_r = msg
-else:
-	def msg(s): sys.stderr.write(s+"\n")
-	def vmsg(s):
-		if opt.verbose: sys.stderr.write(s+"\n")
-	def msg_r(s): sys.stderr.write(s)
-	def vmsg_r(s):
-		if opt.verbose: sys.stderr.write(s)
-
-stderr_save = sys.stderr
-
-def silence():
-	if not (opt.verbose or opt.exact_output):
-		sys.stderr = open("/dev/null","a")
-
-def end_silence():
-	if not (opt.verbose or opt.exact_output):
-		sys.stderr = stderr_save
-
-def errmsg(s): stderr_save.write(s+"\n")
-def errmsg_r(s): stderr_save.write(s)
-
-if opt.list_cmds:
-	fs = "  {:<{w}} - {}"
-	Msg("AVAILABLE COMMANDS:")
-	w = max([len(i) for i in cmd_data])
-	for cmd in cmd_data:
-		Msg(fs.format(cmd,cmd_data[cmd][1],w=w))
-
-	w = max([len(i) for i in meta_cmds])
-	Msg("\nAVAILABLE METACOMMANDS:")
-	for cmd in meta_cmds:
-		Msg(fs.format(cmd," ".join(meta_cmds[cmd]),w=w))
-
-	w = max([len(i) for i in cmd_groups.keys()])
-	Msg("\nAVAILABLE COMMAND GROUPS:")
-	for g in cmd_groups.keys():
-		Msg(fs.format(g," ".join(cmd_groups[g]),w=w))
-
-	Msg("\nAVAILABLE UTILITIES:")
-	w = max([len(i) for i in utils])
-	for cmd in sorted(utils):
-		Msg(fs.format(cmd,utils[cmd],w=w))
-	sys.exit()
-
-import pexpect,time,re
-from mmgen.util import get_data_from_file,write_to_file,get_lines_from_file
-
-def my_send(p,t,delay=send_delay,s=False):
-	if delay: time.sleep(delay)
-	ret = p.send(t) # returns num bytes written
-	if delay: time.sleep(delay)
-	if opt.verbose:
-		ls = "" if opt.debug or not s else " "
-		es = "" if s else "  "
-		msg("%sSEND %s%s" % (ls,es,yellow("'%s'"%t.replace('\n',r'\n'))))
-	return ret
-
-def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
-	quo = "'" if type(s) == str else ""
-
-	if opt.verbose: msg_r("EXPECT %s" % yellow(quo+str(s)+quo))
-	else:       msg_r("+")
-
-	try:
-		if s == '': ret = 0
-		else:
-			f = p.expect if regex else p.expect_exact
-			ret = f(s,timeout=3)
-	except pexpect.TIMEOUT:
-		errmsg(red("\nERROR.  Expect %s%s%s timed out.  Exiting" % (quo,s,quo)))
-		sys.exit(1)
-
-	if opt.debug or (opt.verbose and type(s) != str): msg_r(" ==> %s " % ret)
-
-	if ret == -1:
-		errmsg("Error.  Expect returned %s" % ret)
-		sys.exit(1)
-	else:
-		if t == '':
-			if not nonl: vmsg("")
-		else:
-			my_send(p,t,delay,s)
-		return ret
-
-def get_file_with_ext(ext,mydir,delete=True):
-
-	flist = [os.path.join(mydir,f) for f in os.listdir(mydir)
-				if f == ext or f[-(len(ext)+1):] == "."+ext]
-
-	if not flist: return False
-
-	if len(flist) > 1:
-		if delete:
-			if not opt.quiet:
-				msg("Multiple *.%s files in '%s' - deleting" % (ext,mydir))
-			for f in flist: os.unlink(f)
-		return False
-	else:
-		return flist[0]
-
-def get_addrfile_checksum(display=False):
-	addrfile = get_file_with_ext("addrs",cfg['tmpdir'])
-	silence()
-	from mmgen.addr import AddrInfo
-	chk = AddrInfo(addrfile).checksum
-	if opt.verbose and display: msg("Checksum: %s" % cyan(chk))
-	end_silence()
-	return chk
-
-def verify_checksum_or_exit(checksum,chk):
-	if checksum != chk:
-		errmsg(red("Checksum error: %s" % chk))
-		sys.exit(1)
-	vmsg(green("Checksums match: %s") % (cyan(chk)))
-
-
-class MMGenExpect(object):
-
-	def __init__(self,name,mmgen_cmd,cmd_args=[],extra_desc=""):
-		if not opt.system:
-			mmgen_cmd = os.path.join(os.curdir,mmgen_cmd)
-		desc = (cmd_data[name][1],name)[int(bool(opt.names))]
-		if extra_desc: desc += " " + extra_desc
-		if opt.verbose or opt.exact_output:
-			sys.stderr.write(
-				green("Testing: %s\nExecuting " % desc) +
-				cyan("'%s %s'\n" % (mmgen_cmd," ".join(cmd_args)))
-			)
-		else:
-			msg_r("Testing %s: " % desc)
-
-		if opt.direct_exec:
-			os.system(" ".join([mmgen_cmd] + cmd_args))
-			sys.exit()
-		else:
-			self.p = pexpect.spawn(mmgen_cmd,cmd_args)
-			if opt.exact_output: self.p.logfile = sys.stdout
-
-	def license(self):
-		p = "'w' for conditions and warranty info, or 'c' to continue: "
-		my_expect(self.p,p,'c')
-
-	def label(self,label="Test Label"):
-		p = "Enter a wallet label, or hit ENTER for no label: "
-		my_expect(self.p,p,label+"\n")
-
-	def usr_rand_out(self,saved=False):
-		m = "%suser-supplied entropy" % ("saved " if saved else "")
-		my_expect(self.p,"Generating encryption key from OS random data plus " + m)
-
-	def usr_rand(self,num_chars):
-		rand_chars = list(getrandstr(num_chars,no_space=True))
-		my_expect(self.p,'symbols left: ','x')
-		try:
-			vmsg_r("SEND ")
-			while self.p.expect('left: ',0.1) == 0:
-				ch = rand_chars.pop(0)
-				msg_r(yellow(ch)+" " if opt.verbose else "+")
-				self.p.send(ch)
-		except:
-			vmsg("EOT")
-		my_expect(self.p,"ENTER to continue: ",'\n')
-
-	def passphrase_new(self,desc,passphrase):
-		my_expect(self.p,("Enter passphrase for %s: " % desc), passphrase+"\n")
-		my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
-
-	def passphrase(self,desc,passphrase,pwtype=""):
-		if pwtype: pwtype += " "
-		my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,desc)),
-				passphrase+"\n",regex=True)
-
-	def hash_preset(self,desc,preset=''):
-		my_expect(self.p,("Enter hash preset for %s," % desc))
-		my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
-
-	def written_to_file(self,desc,overwrite_unlikely=False,query="Overwrite?  ",oo=False):
-		s1 = "%s written to file " % desc
-		s2 = query + "Type uppercase 'YES' to confirm: "
-		ret = my_expect(self.p,s1 if overwrite_unlikely else [s1,s2])
-		if ret == 1:
-			my_send(self.p,"YES\n")
-			if oo:
-				outfile = self.expect_getend("Overwriting file '").rstrip("'")
-				return outfile
-			else:
-				ret = my_expect(self.p,s1)
-		outfile = self.p.readline().strip().strip("'")
-		vmsg("%s file: %s" % (desc,cyan(outfile.replace("'",""))))
-		return outfile
-
-	def no_overwrite(self):
-		self.expect("Overwrite?  Type uppercase 'YES' to confirm: ","\n")
-		self.expect("Exiting at user request")
-
-	def tx_view(self):
-		my_expect(self.p,r"View .*?transaction.*? \(y\)es, \(N\)o, pager \(v\)iew.*?: ","\n",regex=True)
-
-	def expect_getend(self,s,regex=False):
-		ret = self.expect(s,regex=regex,nonl=True)
-		end = self.readline().strip()
-		vmsg(" ==> %s" % cyan(end))
-		return end
-
-	def interactive(self):
-		return self.p.interact()
-
-	def logfile(self,arg):
-		self.p.logfile = arg
-
-	def expect(self,*args,**kwargs):
-		return my_expect(self.p,*args,**kwargs)
-
-	def send(self,*args,**kwargs):
-		return my_send(self.p,*args,**kwargs)
-
-	def readline(self):
-		return self.p.readline()
-
-	def close(self):
-		return self.p.close()
-
-	def readlines(self):
-		return [l.rstrip()+"\n" for l in self.p.readlines()]
-
-	def read(self,n=None):
-		return self.p.read(n)
-
-from mmgen.rpc.data import TransactionInfo
-from decimal import Decimal
-from mmgen.bitcoin import verify_addr
-
-def add_fake_unspent_entry(out,address,comment):
-	out.append(TransactionInfo(
-		account = unicode(comment),
-		vout = int(getrandnum(4) % 8),
-		txid = unicode(hexlify(os.urandom(32))),
-		amount = Decimal("%s.%s" % (10+(getrandnum(4) % 40), getrandnum(4) % 100000000)),
-		address = address,
-		spendable = False,
-		scriptPubKey = ("76a914"+verify_addr(address,return_hex=True)+"88ac"),
-		confirmations = getrandnum(4) % 500
-	))
-
-def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input=''):
-
-	out = []
-	for s in tx_data.keys():
-		sid = tx_data[s]['sid']
-		a = adata.addrinfo(sid)
-		for idx,btcaddr in a.addrpairs():
-			add_fake_unspent_entry(out,btcaddr,"%s:%s Test Wallet" % (sid,idx))
-
-	if non_mmgen_input:
-		from mmgen.bitcoin import privnum2addr,hextowif
-		privnum = getrandnum(32)
-		btcaddr = privnum2addr(privnum,compressed=True)
-		of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
-		write_to_file(of, hextowif("{:064x}".format(privnum),
-					compressed=True)+"\n","compressed bitcoin key")
-
-		add_fake_unspent_entry(out,btcaddr,"Non-MMGen address")
-
-#	msg("\n".join([repr(o) for o in out])); sys.exit()
-	write_to_file(unspent_data_file,repr(out),"Unspent outputs",verbose=True)
-
-
-def add_comments_to_addr_file(addrfile,tfile):
-	silence()
-	msg(green("Adding comments to address file '%s'" % addrfile))
-	from mmgen.addr import AddrInfo
-	a = AddrInfo(addrfile)
-	for i in a.idxs(): a.set_comment(idx,"Test address %s" % idx)
-	write_to_file(tfile,a.fmt_data(),{})
-	end_silence()
-
-def make_brainwallet_file(fn):
-	# Print random words with random whitespace in between
-	from mmgen.mn_tirosh import words
-	wl = words.split("\n")
-	nwords,ws_list,max_spaces = 10,"    \n",5
-	def rand_ws_seq():
-		nchars = getrandnum(1) % max_spaces + 1
-		return "".join([ws_list[getrandnum(1)%len(ws_list)] for i in range(nchars)])
-	rand_pairs = [wl[getrandnum(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
-	d = "".join(rand_pairs).rstrip() + "\n"
-	if opt.verbose: msg_r("Brainwallet password:\n%s" % cyan(d))
-	write_to_file(fn,d,"brainwallet password")
-
-def do_between():
-	if opt.pause:
-		from mmgen.util import keypress_confirm
-		if keypress_confirm(green("Continue?"),default_yes=True):
-			if opt.verbose or opt.exact_output: sys.stderr.write("\n")
-		else:
-			errmsg("Exiting at user request")
-			sys.exit()
-	elif opt.verbose or opt.exact_output:
-		sys.stderr.write("\n")
-
-
-rebuild_list = OrderedDict()
-
-def check_needs_rerun(ts,cmd,build=False,root=True,force_delete=False,dpy=False):
-
-	rerun = True if root else False  # force_delete is not passed to recursive call
-
-	fns = []
-	if force_delete or not root:
-		# does cmd produce a needed dependency(ies)?
-		ret = ts.get_num_exts_for_cmd(cmd,dpy)
-		if ret:
-			for ext in ret[1]:
-				fn = get_file_with_ext(ext,cfgs[ret[0]]['tmpdir'],delete=build)
-				if fn:
-					if force_delete: os.unlink(fn)
-					else: fns.append(fn)
-				else: rerun = True
-
-	fdeps = ts.generate_file_deps(cmd)
-	cdeps = ts.generate_cmd_deps(fdeps)
-
-	for fn in fns:
-		my_age = os.stat(fn).st_mtime
-		for num,ext in fdeps:
-			f = get_file_with_ext(ext,cfgs[num]['tmpdir'],delete=build)
-			if f and os.stat(f).st_mtime > my_age: rerun = True
-
-	for cdep in cdeps:
-		if check_needs_rerun(ts,cdep,build=build,root=False,dpy=cmd): rerun = True
-
-	if build:
-		if rerun:
-			for fn in fns:
-				if not root: os.unlink(fn)
-			ts.do_cmd(cmd)
-			if not root: do_between()
-	else:
-		# If prog produces multiple files:
-		if cmd not in rebuild_list or rerun == True:
-			rebuild_list[cmd] = (rerun,fns[0] if fns else "") # FIX
-
-	return rerun
-
-def refcheck(desc,chk,refchk):
-	vmsg("Comparing %s '%s' to stored reference" % (desc,chk))
-	if chk == refchk:
-		ok()
-	else:
-		if not opt.verbose: errmsg("")
-		errmsg(red("""
-Fatal error - %s '%s' does not match reference value '%s'.  Aborting test
-""".strip() % (desc,chk,refchk)))
-		sys.exit(3)
-
-def check_deps(cmds):
-	if len(cmds) != 1:
-		msg("Usage: %s check_deps <command>" % g.prog_name)
-		sys.exit(1)
-
-	cmd = cmds[0]
-
-	if cmd not in cmd_data:
-		msg("'%s': unrecognized command" % cmd)
-		sys.exit(1)
-
-	if not opt.quiet:
-		msg("Checking dependencies for '%s'" % (cmd))
-
-	check_needs_rerun(ts,cmd,build=False)
-
-	w = max(len(i) for i in rebuild_list) + 1
-	for cmd in rebuild_list:
-		c = rebuild_list[cmd]
-		m = "Rebuild" if (c[0] and c[1]) else "Build" if c[0] else "OK"
-		msg("cmd {:<{w}} {}".format(cmd+":", m, w=w))
-#			mmsg(cmd,c)
-
-
-def clean(dirs=[]):
-	ts = MMGenTestSuite()
-	dirlist = ts.list_tmp_dirs()
-	if not dirs: dirs = dirlist.keys()
-	for d in sorted(dirs):
-		if d in dirlist:
-			cleandir(dirlist[d])
-		else:
-			msg("%s: invalid directory number" % d)
-			sys.exit(1)
-
-class MMGenTestSuite(object):
-
-	def __init__(self):
-		pass
-
-	def list_tmp_dirs(self):
-		d = {}
-		for k in cfgs: d[k] = cfgs[k]['tmpdir']
-		return d
-
-	def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
-		num = str(cmd_data[cmd][0])
-		dgl = cfgs[num]['dep_generators']
-#	mmsg(num,cmd,dgl)
-		if cmd in dgl.values():
-			exts = [k for k in dgl if dgl[k] == cmd]
-			return (num,exts)
-		else:
-			return None
-
-	def do_cmd(self,cmd):
-
-		d = [(str(num),ext) for exts,num in cmd_data[cmd][2] for ext in exts]
-		al = [get_file_with_ext(ext,cfgs[num]['tmpdir']) for num,ext in d]
-
-		global cfg
-		cfg = cfgs[str(cmd_data[cmd][0])]
-
-		self.__class__.__dict__[cmd](*([self,cmd] + al))
-
-	def generate_file_deps(self,cmd):
-		return [(str(n),e) for exts,n in cmd_data[cmd][2] for e in exts]
-
-	def generate_cmd_deps(self,fdeps):
-		return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps]
-
-	def walletgen(self,name,brain=False,seed_len=None):
-
-		args = ["-d",cfg['tmpdir'],"-p1","-r10"]
-		if seed_len: args += ["-l",str(seed_len)]
-#        if 'seed_len' in cfg: args += ["-l",cfg['seed_len']]
-		if brain:
-			bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
-			args += ["-b",cfg['bw_params'],bwf]
-			make_brainwallet_file(bwf)
-
-		t = MMGenExpect(name,"mmgen-walletgen", args)
-		t.license()
-
-		if brain:
-			t.expect(
-	"A brainwallet will be secure only if you really know what you're doing")
-			t.expect("Type uppercase 'YES' to confirm: ","YES\n")
-
-		t.usr_rand(10)
-#		t.usr_rand_out()
-
-		t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
-		t.written_to_file("Wallet")
-# 		if not brain:
-# 			t.usr_rand_out(saved=True)
-# 		t.label()
-# 		t.written_to_file("MMGen wallet")
-		ok()
-
-	def refwalletgen(self,name):
-		label = "test.py ref. wallet (pw '%s', seed len %s)" \
-					% (ref_wallet_brainpass,cfg['seed_len'])
-		bw_arg = "-b%s,%s" % (cfg['seed_len'], ref_wallet_hash_preset)
-		args = ["-d",cfg['tmpdir'],"-p1","-r10",bw_arg,"-L",label]
-		d = " (%s-bit seed)" % cfg['seed_len']
-		t = MMGenExpect(name,"mmgen-walletgen", args)
-		t.license()
-		t.expect("Type uppercase 'YES' to confirm: ","YES\n")
-		t.expect("passphrase: ",ref_wallet_brainpass+"\n")
-		t.usr_rand(10)
-		t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
-		seed_id = t.written_to_file("Wallet").split("-")[0].split("/")[-1]
-		refcheck("seed ID",seed_id,cfg['seed_id'])
-
-	refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
-
-	def passchg(self,name,walletfile):
-
-		t = MMGenExpect(name,"mmgen-passchg",
-			["-d",cfg['tmpdir'],"-p","2","-L","New Label","-r","16",walletfile])
-#		t.license()
-		t.passphrase("MMGen wallet",cfgs['1']['wpasswd'],pwtype="old")
-		t.expect_getend("Label changed: ")
-		t.expect_getend("Hash preset changed: ")
-		t.passphrase("MMGen wallet",cfg['wpasswd'],pwtype="new")
-		t.expect("Repeat passphrase: ",cfg['wpasswd']+"\n")
-		t.usr_rand(16)
-		t.expect_getend("Key ID changed: ")
-		t.written_to_file("Wallet")
-		ok()
-
-	def walletchk_beg(self,name,args):
-		t = MMGenExpect(name,"mmgen-walletchk", args)
-		t.expect("Getting MMGen wallet data from file '%s'" % args[-1])
-		t.passphrase("MMGen wallet",cfg['wpasswd'])
-		t.expect("Passphrase is OK")
-		t.expect("Wallet is OK")
-		return t
-
-	def walletchk(self,name,walletfile):
-		self.walletchk_beg(name,[walletfile])
-		ok()
-
-	walletchk_newpass = walletchk
-
-	def addrgen(self,name,walletfile,check_ref=False):
-		t = MMGenExpect(name,"mmgen-addrgen",["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
-		t.license()
-		t.passphrase("MMGen wallet",cfg['wpasswd'])
-		t.expect("Passphrase is OK")
-		t.expect("[0-9]+ addresses generated",regex=True)
-		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
-		if check_ref:
-			refcheck("address data checksum",chk,cfg['addrfile_chk'])
-			return
-		t.written_to_file("Addresses")
-		ok()
-
-	def refaddrgen(self,name,walletfile):
-		d = " (%s-bit seed)" % cfg['seed_len']
-		self.addrgen(name,walletfile,check_ref=True)
-
-	refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen
-
-	def addrimport(self,name,addrfile):
-		outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments")
-		add_comments_to_addr_file(addrfile,outfile)
-		t = MMGenExpect(name,"mmgen-addrimport",[outfile])
-		t.expect_getend(r"Checksum for address data .*\[.*\]: ",regex=True)
-		t.expect_getend("Validating addresses...OK. ")
-		t.expect("Type uppercase 'YES' to confirm: ","\n")
-		vmsg("This is a simulation, so no addresses were actually imported into the tracking\nwallet")
-		ok()
-
-	def txcreate(self,name,addrfile):
-		self.txcreate_common(name,sources=['1'])
-
-	def txcreate_common(self,name,sources=['1'],non_mmgen_input=''):
-		if opt.verbose or opt.exact_output:
-			sys.stderr.write(green("Generating fake transaction info\n"))
-		silence()
-		from mmgen.addr import AddrInfo,AddrInfoList
-		tx_data,ail = {},AddrInfoList()
-		from mmgen.util import parse_addr_idxs
-		for s in sources:
-			afile = get_file_with_ext("addrs",cfgs[s]["tmpdir"])
-			ai = AddrInfo(afile)
-			ail.add(ai)
-			aix = parse_addr_idxs(cfgs[s]['addr_idx_list'])
-			if len(aix) != addrs_per_wallet:
-				errmsg(red("Address index list length != %s: %s" %
-							(addrs_per_wallet,repr(aix))))
-				sys.exit()
-			tx_data[s] = {
-				'addrfile': afile,
-				'chk': ai.checksum,
-				'sid': ai.seed_id,
-				'addr_idxs': aix[-2:],
-			}
-
-		unspent_data_file = os.path.join(cfg['tmpdir'],"unspent.json")
-		create_fake_unspent_data(ail,unspent_data_file,tx_data,non_mmgen_input)
-
-		# make the command line
-		from mmgen.bitcoin import privnum2addr
-		btcaddr = privnum2addr(getrandnum(32),compressed=True)
-
-		cmd_args = ["-d",cfg['tmpdir']]
-		for num in tx_data.keys():
-			s = tx_data[num]
-			cmd_args += [
-				"%s:%s,%s" % (s['sid'],s['addr_idxs'][0],cfgs[num]['amts'][0]),
-			]
-			# + one BTC address
-			# + one change address and one BTC address
-			if num is tx_data.keys()[-1]:
-				cmd_args += ["%s:%s" % (s['sid'],s['addr_idxs'][1])]
-				cmd_args += ["%s,%s" % (btcaddr,cfgs[num]['amts'][1])]
-
-		for num in tx_data: cmd_args += [tx_data[num]['addrfile']]
-
-		os.environ["MMGEN_BOGUS_WALLET_DATA"] = unspent_data_file
-		end_silence()
-		if opt.verbose or opt.exact_output: sys.stderr.write("\n")
-
-		t = MMGenExpect(name,"mmgen-txcreate",cmd_args)
-		t.license()
-		for num in tx_data.keys():
-			t.expect_getend("Getting address data from file ")
-			chk=t.expect_getend(r"Checksum for address data .*?: ",regex=True)
-			verify_checksum_or_exit(tx_data[num]['chk'],chk)
-
-		# not in tracking wallet warning, (1 + num sources) times
-		if t.expect(["Continue anyway? (y/N): ",
-				"Unable to connect to bitcoind"]) == 0:
-			t.send("y")
-		else:
-			errmsg(red("Error: unable to connect to bitcoind.  Exiting"))
-			sys.exit(1)
-
-		for num in tx_data.keys():
-			t.expect("Continue anyway? (y/N): ","y")
-		t.expect(r"'q' = quit sorting, .*?: ","M", regex=True)
-		t.expect(r"'q' = quit sorting, .*?: ","q", regex=True)
-		outputs_list = [addrs_per_wallet*i + 1 for i in range(len(tx_data))]
-		if non_mmgen_input: outputs_list.append(len(tx_data)*addrs_per_wallet + 1)
-		t.expect("Enter a range or space-separated list of outputs to spend: ",
-				" ".join([str(i) for i in outputs_list])+"\n")
-		if non_mmgen_input: t.expect("Accept? (y/N): ","y")
-		t.expect("OK? (Y/n): ","y")
-		t.expect("Add a comment to transaction? (y/N): ","\n")
-		t.tx_view()
-		t.expect("Save transaction? (y/N): ","y")
-		t.written_to_file("Transaction")
-		ok()
-
-	def txsign_end(self,t,tnum=None):
-		t.expect("Signing transaction")
-		t.expect("Edit transaction comment? (y/N): ","\n")
-		t.expect("Save signed transaction? (y/N): ","y")
-		add = " #" + tnum if tnum else ""
-		t.written_to_file("Signed transaction" + add)
-
-	def txsign(self,name,txfile,walletfile,save=True):
-		t = MMGenExpect(name,"mmgen-txsign",
-				["-d",cfg['tmpdir'],txfile,walletfile])
-		t.license()
-		t.tx_view()
-		t.passphrase("MMGen wallet",cfg['wpasswd'])
-		if save:
-			self.txsign_end(t)
-		else:
-			t.expect("Edit transaction comment? (y/N): ","\n")
-			t.expect("Save signed transaction? (y/N): ","\n")
-			t.expect("Signed transaction not saved")
-		ok()
-
-	def txsend(self,name,sigfile):
-		t = MMGenExpect(name,"mmgen-txsend", ["-d",cfg['tmpdir'],sigfile])
-		t.license()
-		t.tx_view()
-		t.expect("Edit transaction comment? (y/N): ","\n")
-		t.expect("broadcast this transaction to the network?")
-		t.expect("'YES, I REALLY WANT TO DO THIS' to confirm: ","\n")
-		t.expect("Exiting at user request")
-		vmsg("This is a simulation; no transaction was sent")
-		ok()
-
-	def export_seed(self,name,walletfile):
-		t = self.walletchk_beg(name,["-s","-d",cfg['tmpdir'],walletfile])
-		f = t.written_to_file("Seed data")
-		silence()
-		msg("Seed data: %s" % cyan(get_data_from_file(f,"seed data")))
-		end_silence()
-		ok()
-
-	def export_mnemonic(self,name,walletfile):
-		t = self.walletchk_beg(name,["-m","-d",cfg['tmpdir'],walletfile])
-		f = t.written_to_file("Mnemonic data")
-		silence()
-		msg_r("Mnemonic data: %s" % cyan(get_data_from_file(f,"mnemonic data")))
-		end_silence()
-		ok()
-
-	def export_incog(self,name,walletfile,args=["-g"]):
-		t = MMGenExpect(name,"mmgen-walletchk",args+["-d",cfg['tmpdir'],"-r","10",walletfile])
-		t.passphrase("MMGen wallet",cfg['wpasswd'])
-		t.usr_rand(10)
-		incog_id = t.expect_getend("Incog ID: ")
-		write_to_tmpfile(cfg,incog_id_fn,incog_id+"\n")
-		if args[0] == "-G": return t
-		t.written_to_file("Incognito wallet data",overwrite_unlikely=True)
-		ok()
-
-	def export_incog_hex(self,name,walletfile):
-		self.export_incog(name,walletfile,args=["-X"])
-
-	# TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
-	def export_incog_hidden(self,name,walletfile):
-		rf,rd = os.path.join(cfg['tmpdir'],hincog_fn),os.urandom(hincog_bytes)
-		vmsg(green("Writing %s bytes of data to file '%s'" % (hincog_bytes,rf)))
-		write_to_file(rf,rd,verbose=opt.verbose)
-		t = self.export_incog(name,walletfile,args=["-G","%s,%s"%(rf,hincog_offset)])
-		t.written_to_file("Data",query="")
-		ok()
-
-	def addrgen_seed(self,name,walletfile,foo,desc="seed data",arg="-s"):
-		t = MMGenExpect(name,"mmgen-addrgen",
-				[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
-		t.license()
-		t.expect_getend("Valid %s for seed ID " % desc)
-		vmsg("Comparing generated checksum with checksum from previous address file")
-		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
-		verify_checksum_or_exit(get_addrfile_checksum(),chk)
-		t.no_overwrite()
-		ok()
-
-	def addrgen_mnemonic(self,name,walletfile,foo):
-		self.addrgen_seed(name,walletfile,foo,desc="mnemonic",arg="-m")
-
-	def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
-		t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
-				cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
-		t.license()
-		t.expect_getend("Incog ID: ")
-		t.passphrase("incognito wallet data \w{8}", cfg['wpasswd'])
-		t.hash_preset("incog wallet",'1')
-		vmsg("Comparing generated checksum with checksum from address file")
-		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
-		verify_checksum_or_exit(get_addrfile_checksum(),chk)
-		t.no_overwrite()
-		ok()
-
-	def addrgen_incog_hex(self,name,walletfile,foo):
-		self.addrgen_incog(name,walletfile,foo,args=["-X"])
-
-	def addrgen_incog_hidden(self,name,walletfile,foo):
-		rf = os.path.join(cfg['tmpdir'],hincog_fn)
-		self.addrgen_incog(name,walletfile,foo,
-				args=["-G","%s,%s,%s"%(rf,hincog_offset,hincog_seedlen)])
-
-	def keyaddrgen(self,name,walletfile,check_ref=False):
-		t = MMGenExpect(name,"mmgen-keygen",
-				["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
-		t.license()
-		t.expect("Type uppercase 'YES' to confirm: ","YES\n")
-		t.passphrase("MMGen wallet",cfg['wpasswd'])
-		chk = t.expect_getend(r"Checksum for key-address data .*?: ",regex=True)
-		if check_ref:
-			refcheck("key-address data checksum",chk,cfg['keyaddrfile_chk'])
-			return
-		t.expect("Encrypt key list? (y/N): ","y")
-		t.hash_preset("new key list",'1')
-		t.passphrase_new("new key list",cfg['kapasswd'])
-		t.written_to_file("Keys")
-		ok()
-
-	def refkeyaddrgen(self,name,walletfile):
-		self.keyaddrgen(name,walletfile,check_ref=True)
-
-	refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen
-
-	def txsign_keyaddr(self,name,keyaddr_file,txfile):
-		t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],"-M",keyaddr_file,txfile])
-		t.license()
-		t.hash_preset("key-address file",'1')
-		t.passphrase("key-address file",cfg['kapasswd'])
-		t.expect("Check key-to-address validity? (y/N): ","y")
-		t.tx_view()
-		self.txsign_end(t)
-		ok()
-
-	def walletgen2(self,name):
-		self.walletgen(name,seed_len=128)
-
-	def addrgen2(self,name,walletfile):
-		self.addrgen(name,walletfile)
-
-	def txcreate2(self,name,addrfile):
-		self.txcreate_common(name,sources=['2'])
-
-	def txsign2(self,name,txf1,wf1,txf2,wf2):
-		t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],txf1,wf1,txf2,wf2])
-		t.license()
-		for cnum in ('1','2'):
-			t.tx_view()
-			t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
-			self.txsign_end(t,cnum)
-		ok()
-
-	def export_mnemonic2(self,name,walletfile):
-		self.export_mnemonic(name,walletfile)
-
-	def walletgen3(self,name):
-		self.walletgen(name)
-
-	def addrgen3(self,name,walletfile):
-		self.addrgen(name,walletfile)
-
-	def txcreate3(self,name,addrfile1,addrfile2):
-		self.txcreate_common(name,sources=['1','3'])
-
-	def txsign3(self,name,wf1,wf2,txf2):
-		t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],wf1,wf2,txf2])
-		t.license()
-		t.tx_view()
-		for cnum in ('1','3'):
-			t.expect_getend("Getting MMGen wallet data from file ")
-			t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
-		self.txsign_end(t)
-		ok()
-
-	def walletgen4(self,name):
-		self.walletgen(name,brain=True)
-
-	def addrgen4(self,name,walletfile):
-		self.addrgen(name,walletfile)
-
-	def txcreate4(self,name,f1,f2,f3,f4):
-		self.txcreate_common(name,sources=['1','2','3','4'],non_mmgen_input='4')
-
-	def txsign4(self,name,f1,f2,f3,f4,f5):
-		non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
-		t = MMGenExpect(name,"mmgen-txsign",
-			["-d",cfg['tmpdir'],"-b",cfg['bw_params'],"-k",non_mm_fn,f1,f2,f3,f4,f5])
-		t.license()
-		t.tx_view()
-
-		for cnum,desc,app in ('1',"incognito","incognito"),('3',"MMGen","MMGen"):
-			t.expect_getend("Getting %s wallet data from file " % desc)
-			t.passphrase("%s wallet"%app,cfgs[cnum]['wpasswd'])
-			if cnum == '1':
-				t.hash_preset("incog wallet",'1')
-
-		self.txsign_end(t)
-		ok()
-
-	def tool_encrypt(self,name,infile=""):
-		if infile:
-			infn = infile
-		else:
-			d = os.urandom(1033)
-			tmp_fn = cfg['tool_enc_infn']
-			write_to_tmpfile(cfg,tmp_fn,d)
-			infn = get_tmpfile_fn(cfg,tmp_fn)
-		t = MMGenExpect(name,"mmgen-tool",["-d",cfg['tmpdir'],"encrypt",infn])
-		t.hash_preset("user data",'1')
-		t.passphrase_new("user data",tool_enc_passwd)
-		t.written_to_file("Encrypted data")
-		ok()
-# Generate the reference mmenc file
-# 	def tool_encrypt_ref(self,name):
-# 		infn = get_tmpfile_fn(cfg,cfg['tool_enc_ref_infn'])
-# 		write_to_file(infn,cfg['tool_enc_reftext'],silent=True)
-# 		self.tool_encrypt(name,infn)
-
-	def tool_decrypt(self,name,f1,f2):
-		of = name + ".out"
-		t = MMGenExpect(name,"mmgen-tool",
-			["-d",cfg['tmpdir'],"decrypt",f2,"outfile="+of,"hash_preset=1"])
-		t.passphrase("user data",tool_enc_passwd)
-		t.written_to_file("Decrypted data")
-		d1 = read_from_file(f1)
-		d2 = read_from_file(get_tmpfile_fn(cfg,of))
-		cmp_or_die(d1,d2)
-
-	def tool_find_incog_data(self,name,f1,f2):
-		i_id = read_from_file(f2).rstrip()
-		vmsg("Incog ID: %s" % cyan(i_id))
-		t = MMGenExpect(name,"mmgen-tool",
-				["-d",cfg['tmpdir'],"find_incog_data",f1,i_id])
-		o = t.expect_getend("Incog data for ID \w{8} found at offset ",regex=True)
-		os.unlink(f1)
-		cmp_or_die(hincog_offset,int(o))
-
-	def walletconv_out(self,name,desc,out_fmt="w",uopts=[],uopts_chk=[],pw=False):
-		opts = ["-d",cfg['tmpdir'],"-r10","-p1","-o",out_fmt] + uopts
-		infile = os.path.join(ref_dir,cfg['seed_id']+".mmwords")
-		d = "(convert)"
-		t = MMGenExpect(name,"mmgen-walletconv",opts+[infile],extra_desc=d)
-		t.license()
-		if pw:
-			t.passphrase_new("new "+desc,cfg['wpasswd'])
-			t.usr_rand(10)
-		if " ".join(desc.split()[-2:]) == "incognito data":
-			for i in (1,2,3):
-				t.expect("Generating encryption key from OS random data ")
-		if desc == "hidden incognito data":
-			ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "],"YES\n")
-			if ret == 0:
-				t.expect("Enter file size: ","1234\n")
-		if out_fmt == "w": t.label()
-		wf = t.written_to_file(capfirst(desc),oo=True)
-		ok()
-
-		d = "(check)"
-		if desc == "hidden incognito data":
-			self.keygen_chksum_chk_hincog(name,cfg['seed_id'],uopts_chk)
-# 		elif pw:
-# 			self.walletchk_chksum_chk(name,wf,cfg['seed_id'],uopts=uopts_chk)
-		else:
-			self.keygen_chksum_chk(name,wf,cfg['seed_id'],pw=pw)
-
-	def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
-		opts = ["-d",cfg['tmpdir'],"-o","words","-r10"]
-		if_arg = [infile] if infile else []
-		d = "(convert)"
-		t = MMGenExpect(name,"mmgen-walletconv",opts+uopts+if_arg,extra_desc=d)
-		t.license()
-		if desc == "brainwallet":
-			t.expect("Enter brainwallet: ",ref_wallet_brainpass+"\n")
-		if pw:
-			t.passphrase(desc,cfg['wpasswd'])
-			if name[:19] == "ref_hincog_conv_old":
-				t.expect("Is the seed ID correct? (Y/n): ","\n")
-			else:
-				t.expect(["Passphrase is OK"," are correct"])
-		# Output
-		wf = t.written_to_file("Mnemonic data",oo=oo)
-		t.close()
-		ok()
-		# back check of result
-		d = "(check)"
-		self.keygen_chksum_chk(name,wf,cfg['seed_id'])
-
-	# Saved reference file tests
-	def ref_wallet_conv(self,name):
-		wf = os.path.join(ref_dir,cfg['ref_wallet'])
-		self.walletconv_in(name,wf,"MMGen wallet",pw=True,oo=True)
-
-	def ref_mn_conv(self,name,ext="mmwords",desc="Mnemonic data"):
-		wf = os.path.join(ref_dir,cfg['seed_id']+"."+ext)
-		self.walletconv_in(name,wf,desc,oo=True)
-
-	def ref_seed_conv(self,name):
-		self.ref_mn_conv(name,ext="mmseed",desc="Seed data")
-
-	def ref_brain_conv(self,name):
-		uopts = ["-i","b","-p","1","-l",str(cfg['seed_len'])]
-		self.walletconv_in(name,None,"brainwallet",uopts,oo=True)
-
-	def ref_incog_conv(self,name,wfk="ic_wallet",in_fmt="i",desc="incognito data"):
-		uopts = ["-i",in_fmt,"-p","1","-l",str(cfg['seed_len'])]
-		wf = os.path.join(ref_dir,cfg[wfk])
-		self.walletconv_in(name,wf,desc,uopts,oo=True,pw=True)
-
-	def ref_incox_conv(self,name):
-		self.ref_incog_conv(name,in_fmt="xi",wfk="ic_wallet_hex",desc="hex incognito data")
-
-	def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
-		ic_f = os.path.join(ref_dir,cfg[wfk])
-		uopts = ["-i","hi","-p","1","-l",str(cfg['seed_len'])] + add_uopts
-		hi_opt = ["-H","%s,%s" % (ic_f,ref_wallet_incog_offset)]
-		self.walletconv_in(name,None,"hidden incognito data",uopts+hi_opt,oo=True,pw=True)
-
-	def ref_hincog_conv_old(self,name):
-		self.ref_hincog_conv(name,wfk='hic_wallet_old',add_uopts=["-O"])
-
-	def ref_wallet_conv_out(self,name):
-		self.walletconv_out(name,"MMGen wallet","w",pw=True)
-
-	def ref_mn_conv_out(self,name):
-		self.walletconv_out(name,"mnemonic data","mn")
-
-	def ref_seed_conv_out(self,name):
-		self.walletconv_out(name,"seed data","seed")
-
-	def ref_incog_conv_out(self,name):
-		self.walletconv_out(name,"incognito data",out_fmt="i",pw=True)
-
-	def ref_incox_conv_out(self,name):
-		self.walletconv_out(name,"hex incognito data",out_fmt="xi",pw=True)
-
-	def ref_hincog_conv_out(self,name,extra_uopts=[]):
-		ic_f = os.path.join(cfg['tmpdir'],"rand.data")
-		hi_parms = "%s,%s" % (ic_f,ref_wallet_incog_offset)
-		hi_parms_legacy = "%s,%s,%s"%(ic_f,ref_wallet_incog_offset,cfg['seed_len'])
-		self.walletconv_out(name,
-			"hidden incognito data", "hi",
-			uopts=["-J",hi_parms] + extra_uopts,
-			uopts_chk=["-G",hi_parms_legacy],
-			pw=True
-		)
-
-	ref_wallet_conv1 = ref_wallet_conv2 = ref_wallet_conv3 = ref_wallet_conv
-	ref_mn_conv1 = ref_mn_conv2 = ref_mn_conv3 = ref_mn_conv
-	ref_seed_conv1 = ref_seed_conv2 = ref_seed_conv3 = ref_seed_conv
-	ref_brain_conv1 = ref_brain_conv2 = ref_brain_conv3 = ref_brain_conv
-	ref_incog_conv1 = ref_incog_conv2 = ref_incog_conv3 = ref_incog_conv
-	ref_incox_conv1 = ref_incox_conv2 = ref_incox_conv3 = ref_incox_conv
-	ref_hincog_conv1 = ref_hincog_conv2 = ref_hincog_conv3 = ref_hincog_conv
-	ref_hincog_conv_old1 = ref_hincog_conv_old2 = ref_hincog_conv_old3 = ref_hincog_conv_old
-
-	ref_wallet_conv_out1 = ref_wallet_conv_out2 = ref_wallet_conv_out3 = ref_wallet_conv_out
-	ref_mn_conv_out1 = ref_mn_conv_out2 = ref_mn_conv_out3 = ref_mn_conv_out
-	ref_seed_conv_out1 = ref_seed_conv_out2 = ref_seed_conv_out3 = ref_seed_conv_out
-	ref_incog_conv_out1 = ref_incog_conv_out2 = ref_incog_conv_out3 = ref_incog_conv_out
-	ref_incox_conv_out1 = ref_incox_conv_out2 = ref_incox_conv_out3 = ref_incox_conv_out
-	ref_hincog_conv_out1 = ref_hincog_conv_out2 = ref_hincog_conv_out3 = ref_hincog_conv_out
-
-	def ref_wallet_chk(self,name):
-		wf = os.path.join(ref_dir,cfg['ref_wallet'])
-		self.walletchk(name,wf)
-
-	ref_wallet_chk1 = ref_wallet_chk2 = ref_wallet_chk3 = ref_wallet_chk
-
-	def ref_seed_chk(self,name,ext=g.seed_ext):
-		wf = os.path.join(ref_dir,"%s.%s" % (cfg['seed_id'],ext))
-		desc = "seed data" if ext == g.seed_ext else "mnemonic"
-		self.keygen_chksum_chk(name,wf,cfg['seed_id'])
-
-	ref_seed_chk1 = ref_seed_chk2 = ref_seed_chk3 = ref_seed_chk
-
-	def ref_mn_chk(self,name): self.ref_seed_chk(name,ext=g.mn_ext)
-
-	ref_mn_chk1 = ref_mn_chk2 = ref_mn_chk3 = ref_mn_chk
-
-	def ref_brain_chk(self,name,bw_file=ref_bw_file):
-		wf = os.path.join(ref_dir,bw_file)
-		arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
-		self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],[arg])
-
-	def keygen_chksum_chk_hincog(self,name,seed_id,hincog_parm):
-		t = MMGenExpect(name,"mmgen-keygen", ["-p1","-q","-S","-A"]+hincog_parm+["1"],extra_desc="(check)")
-		t.passphrase("",cfg['wpasswd'])
-		t.expect("Encrypt key list? (y/N): ","\n")
-		t.expect("any printable ASCII symbol.\r\n")
-		chk = t.readline()[:8]
-		vmsg("Seed ID: %s" % cyan(chk))
-		cmp_or_die(seed_id,chk)
-
-	def keygen_chksum_chk(self,name,wf,seed_id,args=[],pw=False):
-		hp_arg = ["-p1"] if pw else []
-		t = MMGenExpect(name,"mmgen-keygen", ["-q","-S","-A"]+args+hp_arg+[wf,"1"],extra_desc="(check)")
-		if pw:
-			t.passphrase("",cfg['wpasswd'])
-		t.expect("Encrypt key list? (y/N): ","\n")
-		t.expect("any printable ASCII symbol.\r\n")
-		chk = t.readline()[:8]
-		vmsg("Seed ID: %s" % cyan(chk))
-		cmp_or_die(seed_id,chk)
-
-	# Use this for encrypted wallets instead of keygen_chksum_chk()
-	def walletchk_chksum_chk(self,name,wf,seed_id,uopts=[]):
-		t = MMGenExpect(name,"mmgen-walletchk",["-v", wf]+uopts,
-							extra_desc="(check)")
-		t.passphrase("",cfg['wpasswd'])
-		chk = t.expect_getend("Seed ID checksum OK (")[:8]
-		t.close()
-		cmp_or_die(seed_id,chk)
-
-	ref_brain_chk1 = ref_brain_chk2 = ref_brain_chk3 = ref_brain_chk
-
-	def ref_brain_chk_spc3(self,name):
-		self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
-
-	def ref_hincog_chk(self,name):
-		for wtype,desc,earg in ('hic_wallet','',[]), \
-								('hic_wallet_old','(old format)',["-o"]):
-			ic_arg = "%s,%s,%s" % (
-						os.path.join(ref_dir,cfg[wtype]),
-						ref_wallet_incog_offset,cfg['seed_len']
-					)
-			t = MMGenExpect(name,"mmgen-keygen",
-					["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
-			t.passphrase("incognito wallet",cfg['wpasswd'])
-			t.hash_preset("incog wallet","1")
-			if wtype == 'hic_wallet_old':
-				t.expect("Is the seed ID correct? (Y/n): ","\n")
-			chk = t.expect_getend("Valid incog data for seed ID ")
-			t.close()
-			cmp_or_die(cfg['seed_id'],chk)
-
-	ref_hincog_chk1 = ref_hincog_chk2 = ref_hincog_chk3 = ref_hincog_chk
-
-	def ref_addrfile_chk(self,name,ftype="addr"):
-		wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
-		t = MMGenExpect(name,"mmgen-tool",[ftype+"file_chksum",wf])
-		if ftype == "keyaddr":
-			w = "key-address file"
-			t.hash_preset(w,ref_kafile_hash_preset)
-			t.passphrase(w,ref_kafile_pass)
-			t.expect("Check key-to-address validity? (y/N): ","y")
-		o = t.expect_getend("Checksum for .*address data .*: ",regex=True)
-		cmp_or_die(cfg['ref_'+ftype+'file_chksum'],o)
-
-	def ref_keyaddrfile_chk(self,name):
-		self.ref_addrfile_chk(name,ftype="keyaddr")
-
-#	def txcreate8(self,name,addrfile):
-#		self.txcreate_common(name,sources=['8'])
-
-	def ref_tx_chk(self,name):
-		tf = os.path.join(ref_dir,cfg['ref_tx_file'])
-		wf = os.path.join(ref_dir,cfg['ref_wallet'])
-		self.txsign(name,tf,wf,save=False)
-
-	def ref_tool_decrypt(self,name):
-		f = os.path.join(ref_dir,ref_enc_fn)
-		t = MMGenExpect(name,"mmgen-tool",
-				["-q","decrypt",f,"outfile=-","hash_preset=1"])
-		t.passphrase("user data",tool_enc_passwd)
-		t.readline()
-		import re
-		o = re.sub('\r\n','\n',t.read())
-		cmp_or_die(sample_text,o)
-
-# main()
-if opt.pause:
-	import termios,atexit
-	fd = sys.stdin.fileno()
-	old = termios.tcgetattr(fd)
-	def at_exit():
-		termios.tcsetattr(fd, termios.TCSADRAIN, old)
-	atexit.register(at_exit)
-
-start_time = int(time.time())
-ts = MMGenTestSuite()
-
-for cfg in sorted(cfgs): mk_tmpdir(cfgs[cfg])
-
-try:
-	if cmd_args:
-		for arg in cmd_args:
-			if arg in utils:
-				globals()[arg](cmd_args[cmd_args.index(arg)+1:])
-				sys.exit()
-			elif arg in meta_cmds:
-				for cmd in meta_cmds[arg]:
-					check_needs_rerun(ts,cmd,build=True)
-			elif arg in cmd_groups.keys():
-				for cmd in cmd_groups[arg]:
-					check_needs_rerun(ts,cmd,build=True)
-			elif arg in cmd_data:
-				check_needs_rerun(ts,arg,build=True)
-			else:
-				die(1,"%s: unrecognized command" % arg)
-	else:
-		clean()
-		for cmd in cmd_data:
-			ts.do_cmd(cmd)
-			if cmd is not cmd_data.keys()[-1]: do_between()
-except:
-	sys.stderr = stderr_save
-	raise
-
-t = int(time.time()) - start_time
-sys.stderr.write(green(
-	"All requested tests finished OK, elapsed time: %02i:%02i\n"
-	% (t/60,t%60)))

+ 52 - 46
test/test.py

@@ -13,6 +13,7 @@ import mmgen.opt as opt
 from mmgen.util import mmsg,mdie,Msg,die,capfirst
 from mmgen.test import *
 
+tb_cmd = "scripts/traceback.py"
 hincog_fn      = "rand_data"
 hincog_bytes   = 1024*1024
 hincog_offset  = 98765
@@ -28,8 +29,8 @@ ref_wallet_hash_preset = "1"
 ref_wallet_incog_offset = 123
 
 ref_bw_hash_preset = "1"
-ref_bw_file        = "brainwallet"
-ref_bw_file_spc    = "brainwallet-spaced"
+ref_bw_file        = "wallet.mmbrain"
+ref_bw_file_spc    = "wallet-spaced.mmbrain"
 
 ref_kafile_pass        = "kafile password"
 ref_kafile_hash_preset = "1"
@@ -381,8 +382,9 @@ opts_data = {
 -q, --quiet         Produce minimal output.  Suppress dependency info
 -s, --system        Test scripts and modules installed on system rather than
                     those in the repo root
+-t, --traceback     Run the command inside the '{tb_cmd}' script
 -v, --verbose       Produce more verbose output
-""",
+""".format(tb_cmd=tb_cmd),
 	'notes': """
 
 If no command is given, the whole suite of tests is run.
@@ -542,6 +544,9 @@ class MMGenExpect(object):
 			os.system(" ".join([mmgen_cmd] + cmd_args))
 			sys.exit()
 		else:
+			if opt.traceback:
+				cmd_args = [mmgen_cmd] + cmd_args
+				mmgen_cmd = tb_cmd
 			self.p = pexpect.spawn(mmgen_cmd,cmd_args)
 			if opt.exact_output: self.p.logfile = sys.stdout
 
@@ -580,7 +585,7 @@ class MMGenExpect(object):
 				passphrase+"\n",regex=True)
 
 	def hash_preset(self,desc,preset=''):
-		my_expect(self.p,("Enter hash preset for %s," % desc))
+		my_expect(self.p,("Enter hash preset for %s" % desc))
 		my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
 
 	def written_to_file(self,desc,overwrite_unlikely=False,query="Overwrite?  ",oo=False):
@@ -897,12 +902,11 @@ class MMGenTestSuite(object):
 		t.license()
 		t.passphrase("MMGen wallet",cfg['wpasswd'])
 		t.expect("Passphrase is OK")
-		t.expect("[0-9]+ addresses generated",regex=True)
 		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
 		if check_ref:
 			refcheck("address data checksum",chk,cfg['addrfile_chk'])
 			return
-		t.written_to_file("Addresses")
+		t.written_to_file("Addresses",oo=True)
 		ok()
 
 	def refaddrgen(self,name,walletfile):
@@ -1084,11 +1088,11 @@ class MMGenTestSuite(object):
 		self.export_incog(
 			name,wf,desc="hidden incognito data",out_fmt="hi",add_args=add_args)
 
-	def addrgen_seed(self,name,walletfile,foo,desc="seed data",arg="-s"):
+	def addrgen_seed(self,name,walletfile,foo,desc="seed data",in_fmt="seed"):
 		stdout = (False,True)[int(desc=="seed data")] #capture output to screen once
 		add_arg = ([],["-S"])[int(stdout)]
 		t = MMGenExpect(name,"mmgen-addrgen", add_arg +
-				[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
+				["-i"+in_fmt,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
 		t.license()
 		t.expect_getend("Valid %s for seed ID " % desc)
 		vmsg("Comparing generated checksum with checksum from previous address file")
@@ -1098,16 +1102,19 @@ class MMGenTestSuite(object):
 #		t.no_overwrite()
 		ok()
 
-	def addrgen_mnemonic(self,name,walletfile,foo):
-		self.addrgen_seed(name,walletfile,foo,desc="mnemonic",arg="-m")
+	def addrgen_mnemonic(self,name,wf,foo):
+		self.addrgen_seed(name,wf,foo,desc="mnemonic data",in_fmt="words")
 
-	def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
-		t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
-				cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
+	def addrgen_incog(self,name,wf=[],foo="",in_fmt="i",
+						desc="incognito data",args=[]):
+		t = MMGenExpect(name,"mmgen-addrgen",
+				args+["-i"+in_fmt,"-d",cfg['tmpdir']]+
+				([wf] if wf else [])+
+				[cfg['addr_idx_list']])
 		t.license()
-		t.expect_getend("Incog ID: ")
-		t.passphrase("incognito wallet data \w{8}", cfg['wpasswd'])
-		t.hash_preset("incog wallet",'1')
+		t.expect_getend("Incog Wallet ID: ")
+		t.hash_preset(desc,'1')
+		t.passphrase("%s \w{8}" % desc, cfg['wpasswd'])
 		vmsg("Comparing generated checksum with checksum from address file")
 		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
 		t.close()
@@ -1115,19 +1122,18 @@ class MMGenTestSuite(object):
 #		t.no_overwrite()
 		ok()
 
-	def addrgen_incog_hex(self,name,walletfile,foo):
-		self.addrgen_incog(name,walletfile,foo,args=["-X"])
+	def addrgen_incog_hex(self,name,wf,foo):
+		self.addrgen_incog(name,wf,"",in_fmt="xi",desc="hex incognito data")
 
-	def addrgen_incog_hidden(self,name,walletfile,foo):
+	def addrgen_incog_hidden(self,name,wf,foo):
 		rf = os.path.join(cfg['tmpdir'],hincog_fn)
-		self.addrgen_incog(name,walletfile,foo,
-				args=["-G","%s,%s,%s"%(rf,hincog_offset,hincog_seedlen)])
+		self.addrgen_incog(name,[],"",in_fmt="hi",desc="hidden incognito data",
+			args=["-H","%s,%s"%(rf,hincog_offset),"-l",str(hincog_seedlen)])
 
 	def keyaddrgen(self,name,walletfile,check_ref=False):
 		t = MMGenExpect(name,"mmgen-keygen",
 				["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
 		t.license()
-		t.expect("Type uppercase 'YES' to confirm: ","YES\n")
 		t.passphrase("MMGen wallet",cfg['wpasswd'])
 		chk = t.expect_getend(r"Checksum for key-address data .*?: ",regex=True)
 		if check_ref:
@@ -1136,7 +1142,7 @@ class MMGenTestSuite(object):
 		t.expect("Encrypt key list? (y/N): ","y")
 		t.hash_preset("new key list",'1')
 		t.passphrase_new("new key list",cfg['kapasswd'])
-		t.written_to_file("Keys")
+		t.written_to_file("Secret keys",oo=True)
 		ok()
 
 	def refkeyaddrgen(self,name,walletfile):
@@ -1189,7 +1195,7 @@ class MMGenTestSuite(object):
 		t.license()
 		t.tx_view()
 		for cnum in ('1','3'):
-			t.expect_getend("Getting MMGen wallet data from file ")
+#			t.expect_getend("Getting MMGen wallet data from file ")
 			t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
 		self.txsign_end(t)
 		ok()
@@ -1218,15 +1224,12 @@ class MMGenTestSuite(object):
 	def txsign4(self,name,f1,f2,f3,f4,f5):
 		non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
 		t = MMGenExpect(name,"mmgen-txsign",
-			["-d",cfg['tmpdir'],"-b",cfg['bw_params'],"-k",non_mm_fn,f1,f2,f3,f4,f5])
+			["-d",cfg['tmpdir'],"-i","brain","-b"+cfg['bw_params'],"-p1","-k",non_mm_fn,f1,f2,f3,f4,f5])
 		t.license()
 		t.tx_view()
 
-		for cnum,desc,app in ('1',"incognito","incognito"),('3',"MMGen","MMGen"):
-			t.expect_getend("Getting %s wallet data from file " % desc)
-			t.passphrase("%s wallet"%app,cfgs[cnum]['wpasswd'])
-			if cnum == '1':
-				t.hash_preset("incog wallet",'1')
+		for cnum,desc in ('1',"incognito data"),('3',"MMGen wallet"):
+			t.passphrase(("%s" % desc),cfgs[cnum]['wpasswd'])
 
 		self.txsign_end(t)
 		ok()
@@ -1368,11 +1371,11 @@ class MMGenTestSuite(object):
 	def ref_hincog_conv_out(self,name,extra_uopts=[]):
 		ic_f = os.path.join(cfg['tmpdir'],"rand.data")
 		hi_parms = "%s,%s" % (ic_f,ref_wallet_incog_offset)
-		hi_parms_legacy = "%s,%s,%s"%(ic_f,ref_wallet_incog_offset,cfg['seed_len'])
+		sl_parm = "-l" + str(cfg['seed_len'])
 		self.walletconv_out(name,
 			"hidden incognito data", "hi",
-			uopts=["-J",hi_parms] + extra_uopts,
-			uopts_chk=["-G",hi_parms_legacy],
+			uopts=["-J",hi_parms,sl_parm] + extra_uopts,
+			uopts_chk=["-H",hi_parms,sl_parm],
 			pw=True
 		)
 
@@ -1411,8 +1414,8 @@ class MMGenTestSuite(object):
 
 	def ref_brain_chk(self,name,bw_file=ref_bw_file):
 		wf = os.path.join(ref_dir,bw_file)
-		arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
-		self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],[arg])
+		args = ["-l%s" % cfg['seed_len'], "-p"+ref_bw_hash_preset]
+		self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],args)
 
 	def keygen_chksum_chk_hincog(self,name,seed_id,hincog_parm):
 		t = MMGenExpect(name,"mmgen-keygen", ["-p1","-q","-S","-A"]+hincog_parm+["1"],extra_desc="(check)")
@@ -1425,7 +1428,9 @@ class MMGenTestSuite(object):
 
 	def keygen_chksum_chk(self,name,wf,seed_id,args=[],pw=False):
 		hp_arg = ["-p1"] if pw else []
-		t = MMGenExpect(name,"mmgen-keygen", ["-q","-S","-A"]+args+hp_arg+[wf,"1"],extra_desc="(check)")
+		t = MMGenExpect(name,"mmgen-keygen",
+			["-l",str(cfg['seed_len']),"-q","-S","-A"]+args+hp_arg+[wf,"1"],
+			extra_desc="(check)")
 		if pw:
 			t.passphrase("",cfg['wpasswd'])
 		t.expect("Encrypt key list? (y/N): ","\n")
@@ -1448,20 +1453,21 @@ class MMGenTestSuite(object):
 	def ref_brain_chk_spc3(self,name):
 		self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
 
-	def ref_hincog_chk(self,name):
-		for wtype,desc,earg in ('hic_wallet','',[]), \
-								('hic_wallet_old','(old format)',["-o"]):
-			ic_arg = "%s,%s,%s" % (
+	def ref_hincog_chk(self,name,desc="hidden incognito data"):
+		for wtype,edesc,earg in ('hic_wallet','',[]), \
+								('hic_wallet_old','(old format)',["-O"]):
+			ic_arg = "%s,%s" % (
 						os.path.join(ref_dir,cfg[wtype]),
-						ref_wallet_incog_offset,cfg['seed_len']
+						ref_wallet_incog_offset
 					)
-			t = MMGenExpect(name,"mmgen-keygen",
-					["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
-			t.passphrase("incognito wallet",cfg['wpasswd'])
-			t.hash_preset("incog wallet","1")
+			t = MMGenExpect(name,"mmgen-keygen",["-l",str(cfg['seed_len']),
+					"-q","-A"]+earg+["-H"]+[ic_arg]+['1'],extra_desc=edesc)
+			t.hash_preset(desc,"1")
+			t.passphrase(desc,cfg['wpasswd'])
 			if wtype == 'hic_wallet_old':
 				t.expect("Is the seed ID correct? (Y/n): ","\n")
-			chk = t.expect_getend("Valid incog data for seed ID ")
+			chk = t.expect_getend("Seed ID: ")
+			t.expect("Encrypt key list? (y/N): ","\n")
 			t.close()
 			cmp_or_die(cfg['seed_id'],chk)