modified: README.md
modified: mmgen-keygen modified: mmgen-walletchk modified: mmgen/utils.py
This commit is contained in:
parent
43a4c988ad
commit
c8fcbdfcdc
4 changed files with 245 additions and 23 deletions
215
README.md
215
README.md
|
|
@ -1,2 +1,213 @@
|
|||
mmgen
|
||||
=====
|
||||
# mmgen = Multi-Mode GENerator
|
||||
## command-line Bitcoin cold storage solution
|
||||
|
||||
NOTE: for the time being, MMGen should be considered Alpha software.
|
||||
Downloading and testing it out is easy, risk-free and encouraged.
|
||||
However, spending significant amounts of BTC into your mmgen-generated
|
||||
addresses is done at your own risk.
|
||||
|
||||
### Features:
|
||||
|
||||
> As with all deterministic wallets, mmgen can generate an unlimited number
|
||||
> of address/key pairs from a single seed. You back up your wallet only once.
|
||||
|
||||
> MMGen gives you four ways to access your Bitcoins:
|
||||
|
||||
>> 1) with a wallet encrypted using the crack-resistant scrypt hash function
|
||||
>> + AES256. The wallet's password and hash strength can be changed.
|
||||
|
||||
>> 2) from a one-line seed file (unencrypted);
|
||||
|
||||
>> 3) from an Electrum-like mnemonic of 12, 18 or 24 words; or
|
||||
|
||||
>> 4) from a brain password (recommended for expert users only).
|
||||
|
||||
> Furthermore, these methods can all be combined. If you forget your
|
||||
> Electrum-like mnemonic, for example, you can regenerate it and your
|
||||
> keys from a stored wallet or seed. Correspondingly, a lost wallet or
|
||||
> seed can be recovered from the mnemonic.
|
||||
|
||||
> The wallet and seed are short, simple text files suitable for printing
|
||||
> or even writing out by hand. The base-58-encoded seed is short enough
|
||||
> to memorize, providing another brain storage alternative.
|
||||
|
||||
> Implemented as a suite of python scripts, MMGen is super-lightweight.
|
||||
> Combined with bitcoind compiled with the watch-only address feature
|
||||
> (see below), it provides a complete solution for securely storing
|
||||
> Bitcoins offline and tracking and spending them online.
|
||||
|
||||
|
||||
### Instructions for Linux/Unix:
|
||||
|
||||
### Download:
|
||||
> `git clone https://github.com/mmgen/mmgen.git`
|
||||
|
||||
### Install:
|
||||
> Install the ecdsa, scrypt and pycrypto modules:
|
||||
>> `sudo pip install ecdsa scrypt pycrypto`
|
||||
|
||||
> Install mmgen:
|
||||
>> `cd mmgen; sudo ./setup.py install`
|
||||
|
||||
### Getting Started:
|
||||
> On your offline computer:
|
||||
|
||||
> Generate a wallet with a random seed:
|
||||
|
||||
$ mmgen-walletgen
|
||||
...
|
||||
Wallet saved to file '89ABCDEF-76543210[256,3].dat'
|
||||
|
||||
|
||||
> "89ABCDEF" is the Seed ID; "76543210" is the Key ID.
|
||||
> The Seed ID never changes and will be used to identify all
|
||||
> keys/addresses generated by this wallet.
|
||||
> The Key ID changes when the wallet's password or hash preset are changed.
|
||||
> "256" is the seed length; "3" is the scrypt hash preset.
|
||||
> These are configurable.
|
||||
|
||||
|
||||
> Generate ten addresses with the wallet:
|
||||
|
||||
$ mmgen-addrgen 89ABCDEF-76543210[256,3].dat 1-10
|
||||
...
|
||||
Address data saved to file '89ABCDEF[1-10].addrs'
|
||||
|
||||
|
||||
> Note that the address range, "1-10", is indicated in the filename.
|
||||
> To generate addresses 1000 through 2000 (for example), specify
|
||||
> "1000-2000" on the command line and the filename will be
|
||||
> '89ABCDEF[1000-2000].addrs'
|
||||
|
||||
$ cat '89ABCDEF[1-10].addrs'
|
||||
89ABCDEF {
|
||||
1 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
|
||||
2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc
|
||||
3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N
|
||||
4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s
|
||||
5 1PeI55vtp2bX2uKDkAAR2c6ekHNYe4Hcq7
|
||||
6 1FEqfEsSILwXPfMvVvVuUovzTaaST62Mnf
|
||||
7 1LTTzuhMqPLwQ4IGCwwugny6ZMtUQJSJ1
|
||||
8 1F9495H8EJLb54wirgZkVgI47SP7M2RQWv
|
||||
9 1JbrCyt7BdxRE9GX1N7GiEct8UnIjPmpYd
|
||||
10 1H7vVTk4ejUbQXw45I6g5qvPBSe9bsjDqh
|
||||
}
|
||||
|
||||
|
||||
> To store your Bitcoins, spend them into these addresses from whatever
|
||||
> wallets/software you're currently using. If you have lots of BTC,
|
||||
> generate lots of addresses so that each address will have only a
|
||||
> relatively small balance.
|
||||
|
||||
### Spending your stored coins:
|
||||
> Take address 1 out of cold storage by generating a key for it:
|
||||
|
||||
$ mmgen-keygen 89ABCDEF-76543210[256,3].dat 1
|
||||
...
|
||||
Key data saved to file '89ABCDEF[1].akeys'
|
||||
|
||||
$ cat 89ABCDEF[1].akeys
|
||||
89ABCDEF {
|
||||
1 sec: 5JCAfK1pjRoJgmpmd2HEMNwHxAzprGIXeQt8dz5qt3iLvU2KCbS
|
||||
addr: 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
|
||||
}
|
||||
|
||||
> Save the \*.akeys file to a USB stick and transfer it to your online computer.
|
||||
|
||||
> On your online computer, import the secret key into
|
||||
> a running bitcoind or bitcoin-qt:
|
||||
|
||||
$ bitcoind importprivkey 5JCAfK1pjRoJgmpmd2HEMNwHxAzprGIXeQt8dz5qt3iLvU2KCbS
|
||||
|
||||
> That's all there is to it!
|
||||
|
||||
> OPTIONAL: To track balances without exposing secret keys on your
|
||||
> online computer, download and compile sipa's bitcoind patched for
|
||||
> watch-only addresses:
|
||||
|
||||
$ git clone https://github.com/sipa/bitcoin
|
||||
$ git branch mywatchonly remotes/origin/watchonly
|
||||
$ git checkout mywatchonly
|
||||
|
||||
(build, install)
|
||||
|
||||
> Import your addresses from '89ABCDEF[1-10].addrs':
|
||||
|
||||
$ bitcoind importaddress 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE
|
||||
$ bitcoind importaddress 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc
|
||||
$ ...
|
||||
|
||||
### Using the mnemonic and seed features:
|
||||
|
||||
> Using our example above,
|
||||
|
||||
> Generate a mnemonic from the wallet:
|
||||
|
||||
$ mmgen-walletchk -m '89ABCDEF-76543210[256,3].dat'
|
||||
...
|
||||
Mnemonic data saved to file '89ABCDEF.words'
|
||||
|
||||
$ cat 89ABCDEF.words
|
||||
pleasure tumble spider laughter many stumble secret bother
|
||||
after search float relationship path strong curtain savior
|
||||
worst suspend bright touch away dirty measure thorn
|
||||
|
||||
> Note: a 128-bit or 192-bit seed will generate a shorter mnemonic of 12
|
||||
> or 18 words. Generate a wallet with a shorter seed by using
|
||||
> `mmgen-walletgen -l`. Whether you consider 128 or 192 bytes of
|
||||
> entropy to be enough is up to you.
|
||||
|
||||
> Generate addresses 1-11 using the mnemonic instead of the wallet:
|
||||
|
||||
$ mmgen-addrgen -m 89ABCDEF.words 1-11
|
||||
...
|
||||
Address data saved to file '89ABCDEF[1-11].addrs'
|
||||
|
||||
> Compare the first ten addresses with those earlier generated from the
|
||||
> wallet. You'll see they're the same.
|
||||
|
||||
> Recover a lost wallet using the mnemonic:
|
||||
|
||||
$ mmgen-walletgen -m 89ABCDEF.words
|
||||
...
|
||||
Wallet saved to file '89ABCDEF-01234567[256,3].dat'
|
||||
|
||||
> Note that the regenerated wallet has a different Key ID but
|
||||
> of course the same Seed ID.
|
||||
|
||||
> Seeds are generated the same way as mnemonics. Just change the
|
||||
> '-m' option to '-s' in the preceding commands.
|
||||
|
||||
> A seed file for a 256-bit seed looks like this:
|
||||
|
||||
$ cat 8B7392ED.mmseed
|
||||
f4c84b C5ZT wWpT Jsoi wRVw 2dm9 Aftd WLb8 FggQ eC8h Szjd da9L
|
||||
|
||||
> And for a 128-bit seed:
|
||||
|
||||
$ cat 8E0DFB78.mmseed
|
||||
0fe02f XnyC NfPH piuW dQ2d nM47 VU
|
||||
|
||||
> The latter is short enough to be memorized or written down.
|
||||
|
||||
> The first word is a checksum.
|
||||
> To check that you've written or memorized the seed correctly, take the
|
||||
> first 3 bytes of a sha256 hash of the remainder of the line (with
|
||||
> spaces removed).
|
||||
|
||||
#### Mnemonics and seeds — additional information:
|
||||
> Mnemonic and seed data may be entered at the prompt instead of from a
|
||||
> file. Just omit the filename on the command line.
|
||||
|
||||
> Mnemonic and seed data may be printed to standard output instead of a
|
||||
> file with `mmgen-walletchk -S`
|
||||
|
||||
> Mnemonic and seed files may be output to a directory besides the
|
||||
> current one with `mmgen-walletchk -d`
|
||||
|
||||
> Bear in mind that mnemonic and seed data is unencrypted. If it's
|
||||
> compromised, your Bitcoins can easily be stolen. Make sure no one's
|
||||
> looking when you print mnemonic or seed data to screen. Securely
|
||||
> delete your mnemonic and seed files. In Linux, you can achieve
|
||||
> additional security by writing the files to volatile memory in
|
||||
> '/dev/shm' instead of disk.
|
||||
|
|
|
|||
|
|
@ -168,8 +168,8 @@ if 'stdout' in opts:
|
|||
if invoked_as == "keygen" and not 'quiet' in opts:
|
||||
confirm = True
|
||||
else: confirm = False
|
||||
write_to_stdout(addr_data_str,confirm)
|
||||
write_to_stdout(addr_data_str,"secret keys",confirm)
|
||||
elif not sys.stdout.isatty():
|
||||
write_to_stdout(addr_data_str,confirm=False)
|
||||
write_to_stdout(addr_data_str,"secret keys",confirm=False)
|
||||
else:
|
||||
write_addr_data_to_file(seed, addr_data_str, start, end, opts)
|
||||
|
|
|
|||
|
|
@ -32,18 +32,23 @@ help_data = {
|
|||
'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
|
||||
-m, --export-mnemonic Export the wallet's mnemonic to file
|
||||
-s, --export-seed Export the wallet's seed to file
|
||||
-S, --stdout Print seed or mnemonic data to standard output
|
||||
-v, --verbose Produce more verbose output
|
||||
"""
|
||||
}
|
||||
|
||||
short_opts = "hemsv"
|
||||
long_opts = "help","echo_passphrase","export_mnemonic","export_seed","verbose"
|
||||
short_opts = "hd:emsSv"
|
||||
long_opts = "help","outdir=","echo_passphrase","export_mnemonic",\
|
||||
"export_seed","stdout","verbose"
|
||||
|
||||
opts,cmd_args = Opts.process_opts(sys.argv,help_data,short_opts,long_opts)
|
||||
|
||||
check_opts(opts, ('outdir',))
|
||||
|
||||
if len(cmd_args) != 1:
|
||||
msg("One input file must be specified")
|
||||
sys.exit(2)
|
||||
|
|
@ -63,13 +68,7 @@ if 'export_mnemonic' in opts:
|
|||
p = True if debug else False
|
||||
mn = get_mnemonic_from_seed(seed, wl, default_wl, print_info=p)
|
||||
|
||||
write_mnemonic_to_file(mn, seed, opts)
|
||||
|
||||
if debug: print "Mnemonic:\n%s" % " ".join(mn)
|
||||
write_mnemonic(mn, seed, opts)
|
||||
|
||||
if 'export_seed' in opts:
|
||||
write_seed_to_file(seed, opts)
|
||||
|
||||
if debug:
|
||||
from mmgen.bitcoin import b58encode_pad
|
||||
print "Seed: %s" % col4(b58encode_pad(seed))
|
||||
write_seed(seed, opts)
|
||||
|
|
|
|||
|
|
@ -342,14 +342,14 @@ def encrypt_seed(seed, key, opts):
|
|||
return enc_seed
|
||||
|
||||
|
||||
def write_to_stdout(data, confirm=True):
|
||||
def write_to_stdout(data, what, confirm=True):
|
||||
if sys.stdout.isatty() and confirm:
|
||||
confirm_or_exit("",'output secret keys to screen')
|
||||
confirm_or_exit("",'output {} to screen'.format(what))
|
||||
elif not sys.stdout.isatty():
|
||||
import os
|
||||
of = os.readlink("/proc/%d/fd/1" % os.getpid())
|
||||
msg("Writing data to file '%s'" % of)
|
||||
sys.stdout.write("\n" + data)
|
||||
sys.stdout.write(data)
|
||||
|
||||
|
||||
def get_default_wordlist():
|
||||
|
|
@ -386,7 +386,7 @@ def write_to_file(outfile,data,confirm=False):
|
|||
f.close
|
||||
|
||||
|
||||
def write_seed_to_file(seed, opts):
|
||||
def write_seed(seed, opts):
|
||||
|
||||
outfile = "%s.%s" % (make_chksum_8(seed).upper(),seed_ext)
|
||||
if 'outdir' in opts:
|
||||
|
|
@ -396,20 +396,32 @@ def write_seed_to_file(seed, opts):
|
|||
data = col4(b58encode_pad(seed))
|
||||
chk = _make_chksum_6(b58encode_pad(seed))
|
||||
|
||||
write_to_file(outfile, "%s %s\n" % (chk,data))
|
||||
o = "%s %s\n" % (chk,data)
|
||||
|
||||
msg("%s data saved to file '%s'" % ("Seed",outfile))
|
||||
if 'stdout' in opts:
|
||||
write_to_stdout(o,"seed data",confirm=True)
|
||||
elif not sys.stdout.isatty():
|
||||
write_to_stdout(o,"seed data",confirm=False)
|
||||
else:
|
||||
write_to_file(outfile,o)
|
||||
msg("%s data saved to file '%s'" % ("Seed",outfile))
|
||||
|
||||
|
||||
def write_mnemonic_to_file(mn, seed, opts):
|
||||
def write_mnemonic(mn, seed, opts):
|
||||
|
||||
outfile = "%s.words" % make_chksum_8(seed).upper()
|
||||
if 'outdir' in opts:
|
||||
outfile = "%s/%s" % (opts['outdir'], outfile)
|
||||
|
||||
write_to_file(outfile," ".join(mn) + "\n")
|
||||
o = " ".join(mn) + "\n"
|
||||
|
||||
msg("%s data saved to file '%s'" % ("Mnemonic",outfile))
|
||||
if 'stdout' in opts:
|
||||
write_to_stdout(o,"mnemonic data",confirm=True)
|
||||
elif not sys.stdout.isatty():
|
||||
write_to_stdout(o,"mnemonic data",confirm=False)
|
||||
else:
|
||||
write_to_file(outfile,o)
|
||||
msg("%s data saved to file '%s'" % ("Mnemonic",outfile))
|
||||
|
||||
|
||||
def _display_control_data(label,metadata,hash_preset,salt,enc_seed):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue