Long (common) opts added for setting global vars; display with '--longhelp'.

Datadir '~/.mmgen' and config file 'mmgen.cfg' added:
  * Global vars are now overriden in this order:
    1) config file
    2) environmental variables beginning with 'MMGEN_' (listed in globalvars.py)
    3) command line

Always get user entropy, even for non-critical randomness, unless '-r0'.

rpcuser,rpcpassword now override cookie authentication, as with Core.

RPC to remote daemon now supported with '--rpc-host' option.
This commit is contained in:
philemon 2016-11-17 18:10:27 +03:00
commit 456cc1f76c
Signed by untrusted user who does not match committer: mmgen
GPG key ID: 62DBE9E5212F05BE
29 changed files with 507 additions and 328 deletions

43
data_files/mmgen.cfg Normal file
View file

@ -0,0 +1,43 @@
# Configuration file for the MMGen suite
# Everything following a '#' is ignored
################
# User options #
################
# Uncomment to suppress the GPL license prompt:
# no_license true
# Uncomment to disable color output:
# color false
# Uncomment to use testnet instead of mainnet:
# testnet true
# Set the RPC host (the host bitcoind is running on):
# rpc_host localhost
# Set the default hash preset:
# hash_preset 3
# Set the default number of entropy characters to get from user.
# Must be between 10 and 80.
# A value of 0 disables user entropy, but this is not recommended:
# usr_randchars 30
# Set the default transaction fee in BTC:
# tx_fee 0.0003
# Set the transaction fee adjustment factor. Auto-calculated fees are
# multiplied by this value:
# tx_fee_adj 1.0
#####################################################################
# The following options are probably of interest only to developers #
#####################################################################
# Uncomment to display lots of debugging information:
# debug true
# Set the timeout for RPC connections:
# http_timeout 60

View file

@ -20,7 +20,9 @@ Install the secp256k1 library:
Install MMGen:
$ git clone https://github.com/mmgen/mmgen.git
$ cd mmgen; sudo ./setup.py install
$ cd mmgen
$ git checkout -b stable stable_linux
$ sudo ./setup.py install
Install vanitygen (optional):
@ -34,15 +36,18 @@ Install bitcoind:
> To install prebuilt binaries, click [here][01]. To install from source,
> click [here][02].
**NB:** If your offline machine is already disconnected from the Internet,
do the following:
**NB:** Naturally, your offline machine must be connected to the Internet to
retrieve and install the above packages as described above. If your offline
machine is already offline and you wish to leave it that way, then you'll be
forced to take roughly the following steps:
> From your online machine, download the 'python-pip' package from Debian or
> Ubuntu and the Python packages from pypi.python.org/pypi/<packagename>.
> Transfer these files and the git repositories you've cloned to your offline
> computer using a USB stick or other means at your disposal. Now install
> 'python-pip' with 'sudo dpkg -i', unpack each Python module and install it
> using 'sudo ./setup.py install', and install MMGen and vanitygen from the
> From your online machine, download the Debian/Ubuntu packages and their
> dependencies manually from packages.debian.org or packages.ubuntu.com, and the
> Python packages from pypi.python.org/pypi/<packagename>. Transfer these
> files and the git repositories you've cloned to your offline computer using a
> USB stick or other means at your disposal. Install the Debian/Ubuntu packages
> with 'sudo dpkg -i', unpack each Python module and install it using 'sudo
> ./setup.py install', and install MMGen and the secp256k1 library from the
> copied git repositories as described above.
Congratulations, your installation is now complete! Now proceed to [**Getting

View file

@ -1,3 +1,8 @@
***Warning: the MMGen installation process on Windows is not for the faint of
heart, success is not guaranteed and the user experience is less than optimal.
You're urged to use the prebuilt [MMGenLive][20] USB image instead. It's now
the preferred way for all non-Linux users to run MMGen.***
##### Note: The following instructions assume you'll be unpacking all archives to `C:\`, the root directory on most Windows installations. If you choose to unpack to another location, the `cd` commands must be adjusted accordingly.
#### 1. Install the Python interpreter:
@ -83,9 +88,9 @@ Grab the [tarball][14] and unpack it. At the MSYS prompt, run:
#### 8. Install MMGen:
Get the [zip archive][10] from GitHub and unpack it. At the MSYS prompt, run:
Get the [zip archive][10b] from GitHub and unpack it. At the MSYS prompt, run:
$ cd /c/mmgen-master
$ cd /c/mmgen-stable_mswin
$ sudo ./setup.py install
Type:
@ -107,7 +112,9 @@ a new MSYS window to update your path.
[06]: http://www.openssl.org/source/openssl-1.0.1g.tar.gz
[05]: http://www.openssl.org/source/
[10]: https://github.com/mmgen/mmgen/archive/master.zip
[10b]: https://github.com/mmgen/mmgen/archive/stable_mswin.zip
[11]: http://slproweb.com/download/Win32OpenSSL-1_0_1f.exe
[12]: http://www.openssl.org/related/binaries.html
[13]: Getting-Started-with-MMGen
[14]: https://pypi.python.org/pypi/colorama
[20]: https://github.com/mmgen/MMGenLive

View file

@ -1,7 +1,7 @@
***Warning: the MMGen installation process on Windows is not for the faint of
heart, and the user experience is less than optimal. You're urged to use the
prebuilt [MMGenLive][00] USB image instead. It's now the preferred way for all
non-Linux users to run MMGen.***
heart, success is not guaranteed and the user experience is less than optimal.
You're urged to use the prebuilt [MMGenLive][00] USB image instead. It's now
the preferred way for all non-Linux users to run MMGen.***
Install MMGen on Windows by completing the following three steps:

View file

@ -51,7 +51,7 @@ b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
# The 'zero address':
# 1111111111111111111114oLvT2 (use step2 = ('0' * 40) to generate)
import mmgen.globalvars as g
from mmgen.globalvars import g
def pubhex2hexaddr(pubhex):
step1 = sha256(unhexlify(pubhex)).digest()

View file

@ -21,7 +21,7 @@ common.py: Common imports for all MMGen scripts
"""
import sys
import mmgen.globalvars as g
from mmgen.globalvars import g
import mmgen.opts as opts
from mmgen.opts import opt
from mmgen.util import *

View file

@ -186,7 +186,7 @@ def _get_random_data_from_user(uchars):
def get_random(length):
from Crypto import Random
os_rand = Random.new().read(length)
if g.use_urandchars and opt.usr_randchars:
if opt.usr_randchars:
from_what = 'OS random data'
if not g.user_entropy:
g.user_entropy = \

View file

@ -20,101 +20,197 @@
globalvars.py: Constants and configuration options for the MMGen suite
"""
version = '0.8.7a'
release_date = 'November 2016'
import sys,os
# Variables - these might be altered at runtime:
user_entropy = ''
hash_preset = '3'
usr_randchars = 30
use_urandchars = False
from mmgen.obj import BTCAmt
tx_fee = BTCAmt('0.0003')
tx_fee_adj = 1.0
tx_confs = 3
seed_len = 256
http_timeout = 60
# Global vars are set to dfl values in class g.
# They're overridden in this order:
# 1 - config file
# 2 - environmental vars
# 3 - command line
# Constants - these don't change at runtime
class g(object):
# os.getenv() returns None if environmental var is unset
debug = os.getenv('MMGEN_DEBUG')
no_license = os.getenv('MMGEN_NOLICENSE')
bogus_wallet_data = os.getenv('MMGEN_BOGUS_WALLET_DATA')
disable_hold_protect = os.getenv('MMGEN_DISABLE_HOLD_PROTECT')
color = (False,True)[sys.stdout.isatty() and not os.getenv('MMGEN_DISABLE_COLOR')]
testnet = (False,True)[bool(os.getenv('MMGEN_TESTNET'))]
testnet_name = 'testnet3'
def die(ev=0,s=''):
if s: sys.stderr.write(s+'\n')
sys.exit(ev)
# Variables - these might be altered at runtime:
proj_name = 'MMGen'
prog_name = os.path.basename(sys.argv[0])
author = 'Philemon'
email = '<mmgen-py@yandex.com>'
Cdates = '2013-2016'
version = '0.8.7a'
release_date = 'November 2016'
required_opts = [
'quiet','verbose','debug','outdir','echo_passphrase','passwd_file','stdout',
'show_hash_presets','label','keep_passphrase','keep_hash_preset',
'brain_params','b16','usr_randchars'
]
incompatible_opts = (
('quiet','verbose'),
('label','keep_label'),
('tx_id', 'info'),
('tx_id', 'terse_info'),
('batch', 'rescan'),
)
proj_name = 'MMGen'
prog_name = os.path.basename(sys.argv[0])
author = 'Philemon'
email = '<mmgen-py@yandex.com>'
Cdates = '2013-2016'
min_screen_width = 80
minconf = 1
user_entropy = ''
hash_preset = '3'
usr_randchars = 30
# Global var sets user opt:
dfl_vars = 'minconf','seed_len','hash_preset','usr_randchars','debug','tx_confs','tx_fee_adj','tx_fee','key_generator'
tx_fee = BTCAmt('0.0003')
tx_fee_adj = 1.0
tx_confs = 3
# User opt sets global var:
usr_sets_global = ['testnet']
required_opts += usr_sets_global
seed_len = 256
http_timeout = 60
keyconv_exec = 'keyconv'
# Constants - some of these might be overriden, but they don't change thereafter
mins_per_block = 9
passwd_max_tries = 5
debug = False
no_license = False
hold_protect = True
color = (False,True)[sys.stdout.isatty()]
testnet = False
bogus_wallet_data = ''
rpc_host = 'localhost'
testnet_name = 'testnet3'
max_urandchars = 80
_x = os.getenv('MMGEN_MIN_URANDCHARS')
min_urandchars = int(_x) if _x and int(_x) else 10
for k in ('win','linux'):
if sys.platform[:len(k)] == k:
platform = k; break
else:
die(1,"'%s': platform not supported by %s\n" % (sys.platform,proj_name))
seed_lens = 128,192,256
mn_lens = [i / 32 * 3 for i in seed_lens]
if os.getenv('HOME'): # Linux or MSYS
home_dir = os.getenv('HOME')
elif platform == 'win' and os.getenv('HOMEPATH'): # Windows:
home_dir = os.getenv('HOMEPATH')
else:
m = ('$HOME is not set','Neither $HOME nor %HOMEPATH% are set')[platform=='win']
die(2,m + '\nUnable to determine home directory')
mmenc_ext = 'mmenc'
salt_len = 16
aesctr_iv_len = 16
hincog_chk_len = 8
data_dir = (os.path.join(home_dir,'Application Data',proj_name),
os.path.join(home_dir,'.'+proj_name.lower()))[bool(os.getenv('HOME'))]
bitcoin_data_dir = (os.path.join(home_dir,'Application Data','Bitcoin'),
os.path.join(home_dir,'.bitcoin'))[bool(os.getenv('HOME'))]
cfg_file = os.path.join(data_dir,'{}.cfg'.format(proj_name.lower()))
key_generators = 'python-ecdsa','keyconv','secp256k1'
key_generator = 3 # secp256k1 is default
common_opts = ['color','no_license','rpc_host','testnet']
required_opts = [
'quiet','verbose','debug','outdir','echo_passphrase','passwd_file','stdout',
'show_hash_presets','label','keep_passphrase','keep_hash_preset',
'brain_params','b16','usr_randchars'
]
incompatible_opts = (
('quiet','verbose'),
('label','keep_label'),
('tx_id','info'),
('tx_id','terse_info'),
('batch','rescan'),
)
env_opts = (
'MMGEN_BOGUS_WALLET_DATA',
'MMGEN_DEBUG',
'MMGEN_DISABLE_COLOR',
'MMGEN_DISABLE_HOLD_PROTECT',
'MMGEN_MIN_URANDCHARS',
'MMGEN_NO_LICENSE',
'MMGEN_RPC_HOST',
'MMGEN_TESTNET'
)
hash_presets = {
min_screen_width = 80
minconf = 1
# Global var sets user opt:
global_sets_opt = ['minconf','seed_len','hash_preset','usr_randchars','debug',
'tx_confs','tx_fee_adj','tx_fee','key_generator']
keyconv_exec = 'keyconv'
mins_per_block = 9
passwd_max_tries = 5
max_urandchars = 80
min_urandchars = 10
seed_lens = 128,192,256
mn_lens = [i / 32 * 3 for i in seed_lens]
mmenc_ext = 'mmenc'
salt_len = 16
aesctr_iv_len = 16
hincog_chk_len = 8
key_generators = 'python-ecdsa','keyconv','secp256k1' # 1,2,3
key_generator = 3 # secp256k1 is default
hash_presets = {
# Scrypt params:
# ID N p r
# N is a power of two
'1': [12, 8, 1],
'2': [13, 8, 4],
'3': [14, 8, 8],
'4': [15, 8, 12],
'5': [16, 8, 16],
'6': [17, 8, 20],
'7': [18, 8, 24],
}
'1': [12, 8, 1],
'2': [13, 8, 4],
'3': [14, 8, 8],
'4': [15, 8, 12],
'5': [16, 8, 16],
'6': [17, 8, 20],
'7': [18, 8, 24],
}
for k in ('win','linux'):
if sys.platform[:len(k)] == k: platform = k; break
else:
sys.stderr.write("'%s': platform not supported by %s\n" % (sys.platform,proj_name))
sys.exit(1)
def create_data_dir(g):
from mmgen.util import msg,die
try:
os.listdir(g.data_dir)
except:
try:
os.mkdir(g.data_dir,0700)
except:
die(2,"ERROR: unable to read or create '{}'".format(g.data_dir))
def get_data_from_config_file(g):
from mmgen.util import msg,die
# https://wiki.debian.org/Python:
# Debian (Ubuntu) sys.prefix is '/usr' rather than '/usr/local, so add 'local'
# TODO - test for Windows
# This must match the configuration in setup.py
data = u''
try:
with open(g.cfg_file,'rb') as f: data = f.read().decode('utf8')
except:
cfg_template = os.path.join(*([sys.prefix]
+ ([''],['local','share'])[bool(os.getenv('HOME'))]
+ [g.proj_name.lower(),os.path.basename(g.cfg_file)]))
try:
with open(cfg_template,'rb') as f: template_data = f.read()
except:
msg("WARNING: configuration template not found at '{}'".format(cfg_template))
else:
try:
with open(g.cfg_file,'wb') as f: f.write(template_data)
os.chmod(g.cfg_file,0600)
except:
die(2,"ERROR: unable to write to datadir '{}'".format(g.data_dir))
return data
def override_from_cfg_file(g,cfg_data):
from mmgen.util import die,strip_comments,set_for_type
cvars = ('color','debug','hash_preset','http_timeout','no_license','rpc_host',
'testnet','tx_fee','tx_fee_adj','usr_randchars')
import re
for n,l in enumerate(cfg_data.splitlines(),1): # DOS-safe
l = strip_comments(l)
if l == '': continue
m = re.match(r'(\w+)\s+(\S+)$',l)
if not m: die(2,"Parse error in file '{}', line {}".format(g.cfg_file,n))
name,val = m.groups()
if name in cvars:
setattr(g,name,set_for_type(val,getattr(g,name),name,src=g.cfg_file))
else:
die(2,"'{}': unrecognized option in '{}'".format(name,g.cfg_file))
def override_from_env(g):
from mmgen.util import set_for_type
for name in g.env_opts:
idx,invert_bool = ((6,False),(14,True))[name[:14]=='MMGEN_DISABLE_']
val = os.getenv(name) # os.getenv() returns None if env var is unset
if val:
gname = name[idx:].lower()
setattr(g,gname,set_for_type(val,getattr(g,gname),name,invert_bool))
create_data_dir(g)
cfg_data = get_data_from_config_file(g)
override_from_cfg_file(g,cfg_data)
override_from_env(g)

View file

@ -20,7 +20,7 @@
license.py: Copyright notice and text of GPLv3
"""
import mmgen.globalvars as g
from mmgen.globalvars import g
warning = """
{pnm} Copyright (C) {g.Cdates} by {g.author} {g.email}. This

View file

@ -34,7 +34,7 @@ By default, both addresses and secret keys are generated.
""".strip()
else:
gen_what = 'addresses'
opt_filter = 'hbcdeiHOKlpzPqSv-'
opt_filter = 'hbcdeiHOKlpzPqrSv-'
note1 = """
If available, the external 'keyconv' program will be used for address
generation.
@ -46,29 +46,31 @@ opts_data = {
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.
-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, --help Print this help message
--, --longhelp Print help message for long options (common options)
-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, --key-generator=m Use method 'm' for public key generation.
'f' at offset 'o' (comma-separated)
-O, --old-incog-fmt Specify old-format incognito input
-K, --key-generator=m Use method 'm' for public key generation
Options: {kgs} (default: {kg})
-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.
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.
--, --testnet Generate testnet keys/addresses instead of mainnet ones
-v, --verbose Produce more verbose output.
-x, --b16 Print secret keys in hexadecimal too.
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
-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 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,

View file

@ -34,6 +34,7 @@ opts_data = {
'usage':'[opts] [mmgen address file]',
'options': """
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-b, --batch Import all addresses in one RPC call.
-l, --addrlist Address source is a flat list of (non-MMGen) Bitcoin addresses
-k, --keyaddr-file Address source is a key-address file
@ -41,7 +42,6 @@ opts_data = {
-r, --rescan Rescan the blockchain. Required if address to import is
on the blockchain and has a balance. Rescanning is slow.
-t, --test Simulate operation; don't actually import addresses
--, --testnet Use Bitcoin testnet instead of mainnet
""",
'notes': """\n
This command can also be used to update the comment fields of addresses already

View file

@ -30,11 +30,11 @@ opts_data = {
'options': """
-d, --outdir= d Specify an alternate directory 'd' for output
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-P, --passwd-file= f Get passphrase from file 'f'.
-q, --quiet Produce quieter output
-r, --usr-randchars=n Get 'n' characters of additional randomness from
user (min={g.min_urandchars}, max={g.max_urandchars})
--, --testnet Use Bitcoin testnet instead of mainnet
-v, --verbose Produce more verbose output
""".format(g=g),
'notes': """

View file

@ -32,6 +32,7 @@ opts_data = {
'usage': '[opts] <addr,amt> ... [change addr] [addr file] ...',
'options': """
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-a, --tx-fee-adj= f Adjust transaction fee by factor 'f' (see below)
-B, --no-blank Don't blank screen before displaying unspent outputs
-c, --comment-file= f Source the transaction's comment from file 'f'
@ -41,7 +42,6 @@ opts_data = {
-m, --minconf= n Minimum number of confirmations required to spend outputs (default: 1)
-i, --info Display unspent outputs and exit
-q, --quiet Suppress warnings; overwrite files without prompting
--, --testnet Create transaction for Bitcoin testnet instead of mainnet
-v, --verbose Produce more verbose output
""".format(g=g),
'notes': """

View file

@ -29,9 +29,9 @@ opts_data = {
'usage': '[opts] <signed transaction file>',
'options': """
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-d, --outdir= d Specify an alternate directory 'd' for output
-q, --quiet Suppress warnings; overwrite files without prompting
--, --testnet Use Bitcoin testnet instead of mainnet
"""
}

View file

@ -33,37 +33,35 @@ opts_data = {
'desc': 'Sign Bitcoin transactions generated by {pnl}-txcreate'.format(pnl=pnm.lower()),
'usage': '[opts] <transaction file>... [seed source]...',
'options': """
-h, --help Print this help message.
-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brain-
wallet input.
-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, --help Print this help message
--, --longhelp Print help message for long options (common options)
-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brainwallet
input
-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
'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.
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, --key-generator=m Use method 'm' for public key generation.
-K, --key-generator=m Use method 'm' for public key generation
Options: {kgs} (default: {kg})
-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.
online signing without an {pnm} seed source. The
key-address file is also used to verify {pnm}-to-BTC
mappings, so the user should record its checksum.
-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.
--, --testnet Transaction is for Bitcoin testnet rather than mainnet
-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
""".format(
g=g,pnm=pnm,pnl=pnm.lower(),

View file

@ -38,7 +38,7 @@ pw_note = opts.pw_note
if invoked_as == 'gen':
desc = 'Generate an {pnm} wallet from a random seed'
opt_filter = 'ehdoJlLpPqrSvz'
opt_filter = 'ehdoJlLpPqrSvz-'
usage = '[opts]'
oaction = 'output'
nargs = 0
@ -47,11 +47,11 @@ elif invoked_as == 'conv':
opt_filter = None
elif invoked_as == 'chk':
desc = 'Check validity of an {pnm} wallet'
opt_filter = 'ehiHOlpPqrvz'
opt_filter = 'ehiHOlpPqrvz-'
iaction = 'input'
elif invoked_as == 'passchg':
desc = 'Change the password, hash preset or label of an {pnm} wallet'
opt_filter = 'ehdiHkKOlLmpPqrSvz'
opt_filter = 'ehdiHkKOlLmpPqrSvz-'
iaction = 'input'
bw_note = ''
else:
@ -63,34 +63,34 @@ opts_data = {
'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, --help Print this help message
--, --longhelp Print help message for long options (common options)
-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
'f' at offset 'o' (comma-separated).
-J, --hidden-incog-output-params=f,o Write hidden incognito data to file
'f' at offset 'o' (comma-separated). If file 'f'
doesn't exist, it will be created and filled with
random data.
-O, --old-incog-fmt Specify old-format incognito input.
-k, --keep-passphrase Reuse passphrase of input wallet for output wallet.
-K, --keep-hash-preset Reuse hash preset of input wallet for output wallet.
'f' at offset 'o' (comma-separated)
-J, --hidden-incog-output-params=f,o Write hidden incognito data to file 'f'
at offset 'o' (comma-separated). File 'f' will be cre-
ated and filled with random data if it doesn't exist.
-O, --old-incog-fmt Specify old-format incognito input
-k, --keep-passphrase Reuse passphrase of input wallet for output wallet
-K, --keep-hash-preset Reuse hash preset of input wallet for output wallet
-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.
-L, --label= l Specify a label 'l' for output wallet.
-m, --keep-label Reuse label of input wallet for output wallet.
-L, --label= l Specify a label 'l' for output wallet
-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}').
-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.
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
-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.
(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
""".format(
g=g,
iaction=capfirst(iaction),

View file

@ -92,6 +92,7 @@ class MMGenObject(object):
return repr(self) + '\n ' + '\n '.join(out)
# Descriptor: https://docs.python.org/2/howto/descriptor.html
class MMGenListItemAttr(object):
def __init__(self,name,dtype):
self.name = name
@ -109,7 +110,7 @@ class MMGenListItem(MMGenObject):
addr = MMGenListItemAttr('addr','BTCAddr')
amt = MMGenListItemAttr('amt','BTCAmt')
mmid = MMGenListItemAttr('mmid','MMGenID')
label = MMGenListItemAttr('label','MMGenLabel')
label = MMGenListItemAttr('label','MMGenAddrLabel')
attrs = ()
attrs_priv = ()
@ -269,7 +270,7 @@ class Hilite(object):
@classmethod
def colorize(cls,s,color=True):
import mmgen.globalvars as g
from mmgen.globalvars import g
from mmgen.util import red,blue,green,yellow,pink,cyan,gray,orange,magenta
k = color if type(color) is str else cls.color # hack: override color with str value
return locals()[k](s) if (color or cls.color_always) and g.color else s

View file

@ -23,7 +23,7 @@ import sys
class opt(object): pass
import mmgen.globalvars as g
from mmgen.globalvars import g
import mmgen.share.Opts
from mmgen.util import *
@ -58,34 +58,34 @@ def die_on_incompatible_opts(incompat_list):
if len(bad) > 1:
die(1,'Conflicting options: %s' % ', '.join([fmt_opt(b) for b in bad]))
def _typeconvert_from_dfl(key):
global opt
gval = g.__dict__[key]
uval = opt.__dict__[key]
gtype = type(gval)
try:
setattr(opt,key,gtype(uval))
except:
d = {
'int': 'an integer',
'str': 'a string',
'float': 'a float',
'bool': 'a boolean value',
}
die(1, "'%s': invalid parameter for '--%s' option (not %s)" % (
uval,
key.replace('_','-'),
d[gtype.__name__]
))
if g.debug:
Msg('Opt overriden by user:\n %-18s: %s' % (
key, ('%s -> %s' % (gval,uval))
))
# def _typeconvert_from_dfl(key):
#
# global opt
#
# gval = g.__dict__[key]
# uval = opt.__dict__[key]
# gtype = type(gval)
#
# try:
# setattr(opt,key,gtype(uval))
# except:
# d = {
# 'int': 'an integer',
# 'str': 'a string',
# 'float': 'a float',
# 'bool': 'a boolean value',
# }
# die(1, "'%s': invalid parameter for '--%s' option (not %s)" % (
# uval,
# key.replace('_','-'),
# d[gtype.__name__]
# ))
#
# if g.debug:
# Msg('Opt overriden by user:\n %-18s: %s' % (
# key, ('%s -> %s' % (gval,uval))
# ))
#
def fmt_opt(o): return '--' + o.replace('_','-')
def _show_hash_presets():
@ -96,17 +96,27 @@ def _show_hash_presets():
msg(fs.format("'%s'" % i, *g.hash_presets[i]))
msg('N = memory usage (power of two), p = iterations (rounds)')
common_opts_data = """
--, --color=b Set 'b' to '0' to disable color output, '1' to enable
--, --no-license Suppress the GPL license prompt
--, --rpc-host=h Communicate with bitcoind running on host 'h'
--, --testnet Use testnet instead of mainnet
"""
def init(opts_data,add_opts=[],opt_filter=None):
if len(sys.argv) == 2 and sys.argv[1] == '--version':
print_version_info()
sys.exit()
opts_data['long_options'] = common_opts_data
uopts,args,short_opts,long_opts,skipped_opts = \
mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter)
if g.debug:
d = (
('Cmdline', ' '.join(sys.argv)),
('Short opts', short_opts),
('Long opts', long_opts),
('Skipped opts', skipped_opts),
@ -127,20 +137,21 @@ def init(opts_data,add_opts=[],opt_filter=None):
# Transfer uopts into opt, setting program's opts + required opts to None if not set by user
for o in [s.rstrip('=') for s in long_opts] + \
g.required_opts + add_opts + skipped_opts:
g.required_opts + add_opts + skipped_opts + g.common_opts:
setattr(opt,o,uopts[o] if o in uopts else None)
# User opt sets global var - do these here, before opt gets set from g.dfl_vars
if opt.usr_randchars: g.use_urandchars = True
for k in g.usr_sets_global:
if getattr(opt,k): setattr(g,k,True)
# User opt sets global var - do these here, before opt is set from g.global_sets_opt
for k in g.common_opts:
val = getattr(opt,k)
if val != None: setattr(g,k,set_for_type(val,getattr(g,k),'--'+k))
# If user opt is set, convert its type based on value in mmgen.globalvars (g)
# If unset, set it to default value in mmgen.globalvars (g)
setattr(opt,'set_by_user',[])
for k in g.dfl_vars:
if k in opt.__dict__ and opt.__dict__[k] != None:
_typeconvert_from_dfl(k)
for k in g.global_sets_opt:
if k in opt.__dict__ and getattr(opt,k) != None:
# _typeconvert_from_dfl(k)
setattr(opt,k,set_for_type(getattr(opt,k),getattr(g,k),'--'+k))
opt.set_by_user.append(k)
else:
setattr(opt,k,g.__dict__[k])
@ -153,9 +164,8 @@ def init(opts_data,add_opts=[],opt_filter=None):
_show_hash_presets()
sys.exit()
if opt.debug: opt.verbose = True
if g.debug:
opt.verbose = True
a = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) != None]
b = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) == None]
Msg(' Opts after processing:')

View file

@ -32,14 +32,14 @@ class BitcoinRPCConnection(object):
def __init__(
self,
host='localhost',port=(8332,18332)[g.testnet],
host=g.rpc_host,port=(8332,18332)[g.testnet],
user=None,passwd=None,auth_cookie=None,
):
if auth_cookie:
self.auth_str = auth_cookie
elif user and passwd:
if user and passwd:
self.auth_str = '{}:{}'.format(user,passwd)
elif auth_cookie:
self.auth_str = auth_cookie
else:
msg('Error: no Bitcoin RPC authentication method found')
if passwd: die(1,"'rpcuser' entry missing in bitcoin.conf")

View file

@ -114,7 +114,6 @@ class SeedSource(MMGenObject):
self.msg.update(c._msg)
if hasattr(self,'seed'):
g.use_urandchars = True
self._encrypt()
return
elif hasattr(self,'infile'):

View file

@ -21,20 +21,21 @@ Opts.py: Generic options handling
"""
import sys, getopt
from mmgen.util import pp_die,pp_msg # DEBUG
# from mmgen.util import mdie,die,pp_die,pp_msg # DEBUG
def usage(opts_data):
print 'USAGE: %s %s' % (opts_data['prog_name'], opts_data['usage'])
sys.exit(2)
def print_help(opts_data):
def print_help(opts_data,longhelp=False):
pn = opts_data['prog_name']
pn_len = str(len(pn)+2)
print (' %-'+pn_len+'s %s') % (pn.upper()+':', opts_data['desc'].strip())
print (' %-'+pn_len+'s %s %s')%('USAGE:', pn, opts_data['usage'].strip())
sep = '\n '
print ' OPTIONS:' + sep + sep.join(opts_data['options'].strip().splitlines())
if 'notes' in opts_data:
od_opts = opts_data[('options','long_options')[longhelp]].strip().splitlines()
sep,m,ls = (('\n ',' OPTIONS:',''),('\n',' LONG OPTIONS:',' '))[longhelp]
print m + sep + ls + sep.join(od_opts)
if 'notes' in opts_data and not longhelp:
print ' ' + '\n '.join(opts_data['notes'][1:-1].splitlines())
@ -44,19 +45,17 @@ 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]
# pp_msg(long_opts) # DEBUG
try: cl_opts,args = getopt.getopt(argv[1:], short_opts.replace('-',''), long_opts)
so_str = short_opts.replace('-:','').replace('-','')
try: cl_opts,args = getopt.getopt(argv[1:], so_str, long_opts)
except getopt.GetoptError as err:
print str(err); sys.exit(2)
sopts_list = ':_'.join(['_'.join(list(i)) for i in short_opts.split(':')]).split('_')
opts = {}
# pp_msg(cl_opts) # DEBUG
# pp_msg(sopts_list) # DEBUG
# pp_die(args)
for opt, arg in cl_opts:
if opt in ('-h','--help'): print_help(opts_data); sys.exit()
elif opt == '--longhelp': print_help(opts_data,longhelp=True); sys.exit()
elif opt[:2] == '--' and opt[2:] in long_opts:
opts[opt[2:].replace('-','_')] = True
elif opt[:2] == '--' and opt[2:]+'=' in long_opts:
@ -90,30 +89,28 @@ def parse_opts(argv,opts_data,opt_filter=None):
import re
pat = r'^-([a-zA-Z0-9-]), --([a-zA-Z0-9-]{2,64})(=| )(.+)'
od,skip = [],True
od_all = []
for l in opts_data['options'].strip().splitlines():
m = re.match(pat,l)
if m:
skip = (False,True)[bool(opt_filter) and m.group(1) not in opt_filter]
app = (['',''],[':','='])[m.group(3) == '=']
od.append(list(m.groups()) + app + [skip])
else:
if not skip: od[-1][3] += '\n' + l
for k in ('options','long_options'):
od,skip = [],True
for l in opts_data[k].strip().splitlines():
m = re.match(pat,l)
if m:
skip = (False,True)[bool(opt_filter) and m.group(1) not in opt_filter]
app = (['',''],[':','='])[m.group(3) == '=']
od.append(list(m.groups()) + app + [skip])
else:
if not skip: od[-1][3] += '\n' + l
opts_data['options'] = '\n'.join(
['{:<3} --{} {}'.format(
('-'+d[0]+',','')[d[0]=='-'],d[1],d[3]) for d in od if d[6] == False]
)
# print opts_data['options']; sys.exit() # DEBUG
# pp_die(od) # DEBUG
short_opts = ''.join([d[0]+d[4] for d in od if d[6] == False])
long_opts = [d[1].replace('-','_')+d[5] for d in od if d[6] == False]
skipped_opts = [d[1].replace('-','_') for d in od if d[6] == True]
# pp_die(short_opts) # DEBUG
# pp_msg(long_opts) # DEBUG
opts_data[k] = '\n'.join(
['{:<3} --{} {}'.format(
('-'+d[0]+',','')[d[0]=='-'],d[1],d[3]) for d in od if d[6] == False]
)
od_all += od
short_opts = ''.join([d[0]+d[4] for d in od_all if d[6] == False])
long_opts = [d[1].replace('-','_')+d[5] for d in od_all if d[6] == False]
skipped_opts = [d[1].replace('-','_') for d in od_all if d[6] == True]
opts,args = process_opts(argv,opts_data,short_opts,long_opts)
# pp_die(opts) # DEBUG
return opts,args,short_opts,long_opts,skipped_opts

View file

@ -179,24 +179,24 @@ def mswin_dummy_flush(fd,termconst): pass
try:
import tty,termios
from select import select
if g.disable_hold_protect:
get_char = _get_keypress_unix_raw
kb_hold_protect = _kb_hold_protect_unix_raw
else:
if g.hold_protect:
get_char = _get_keypress_unix
kb_hold_protect = _kb_hold_protect_unix
else:
get_char = _get_keypress_unix_raw
kb_hold_protect = _kb_hold_protect_unix_raw
get_terminal_size = _get_terminal_size_linux
myflush = termios.tcflush
# call: myflush(sys.stdin, termios.TCIOFLUSH)
except:
try:
import msvcrt,time
if g.disable_hold_protect:
get_char = _get_keypress_mswin_raw
kb_hold_protect = _kb_hold_protect_mswin_raw
else:
if g.hold_protect:
get_char = _get_keypress_mswin
kb_hold_protect = _kb_hold_protect_mswin
else:
get_char = _get_keypress_mswin_raw
kb_hold_protect = _kb_hold_protect_mswin_raw
get_terminal_size = _get_terminal_size_mswin
myflush = mswin_dummy_flush
except:
@ -222,9 +222,9 @@ def do_pager(text):
shell = True
pagers = ['more']
else: # MSYS
environ['LESS'] = '-cR' # disable buggy line chopping
environ['LESS'] = '-cR -#1' # disable buggy line chopping
else:
environ['LESS'] = '-RS'
environ['LESS'] = '-RS -#1' # raw, chop, scroll right 1 char
if 'PAGER' in environ and environ['PAGER'] != pagers[0]:
pagers = [environ['PAGER']] + pagers

View file

@ -25,10 +25,6 @@ from hashlib import sha256
from binascii import hexlify,unhexlify
from string import hexdigits
import mmgen.globalvars as g
pnm = g.proj_name
# If 88- or 256-color support is compiled, the following apply.
# P s = 3 8 ; 5 ; P s -> Set foreground color to the second P s .
# P s = 4 8 ; 5 ; P s -> Set background color to the second P s .
@ -56,19 +52,6 @@ def gray(s): return _gry+s+_reset
def magenta(s): return _mag+s+_reset
def nocolor(s): return s
def start_mscolor():
if g.platform == 'win':
global red,green,yellow,cyan,nocolor
import os
if 'MMGEN_NOMSCOLOR' in os.environ:
red = green = yellow = cyan = grnbg = nocolor
else:
try:
import colorama
colorama.init(strip=True,convert=True)
except:
red = green = yellow = cyan = grnbg = nocolor
def msg(s): sys.stderr.write(s+'\n')
def msg_r(s): sys.stderr.write(s)
def Msg(s): sys.stdout.write(s + '\n')
@ -83,9 +66,11 @@ def mdie(*args):
sys.exit()
def die(ev=0,s=''):
assert type(ev) == int
if s: sys.stderr.write(s+'\n')
sys.exit(ev)
def Die(ev=0,s=''):
assert type(ev) == int
if s: sys.stdout.write(s+'\n')
sys.exit(ev)
@ -101,6 +86,23 @@ def pp_msg(d):
import pprint
msg(pprint.PrettyPrinter(indent=4).pformat(d))
def set_for_type(val,refval,desc,invert_bool=False,src=None):
src_str = (''," in '{}'".format(src))[bool(src)]
if type(refval) == bool:
v = str(val).lower()
if v in ('true','yes','1'): ret = True
elif v in ('false','no','none','0'): ret = False
else: die(1,"'{}': invalid value for '{}'{} (must be of type '{}')".format(
val,desc,src_str,'bool'))
if invert_bool: ret = not ret
else:
try:
ret = type(refval)((val,not val)[invert_bool])
except:
die(1,"'{}': invalid value for '{}'{} (must be of type '{}')".format(
val,desc,src_str,type(refval).__name__))
return ret
# From 'man dd':
# c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
# GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
@ -265,18 +267,6 @@ def file_is_readable(f):
else:
return True
def get_homedir():
if 'HOME' in os.environ: # Linux
return os.environ['HOME']
elif 'HOMEPATH' in os.environ: # Windows:
return os.environ['HOMEPATH']
else:
msg('Neither $HOME nor %HOMEPATH% are set')
die(2,"Don't know where to look for bitcoin data directory")
def get_datadir():
return (r'Application Data\Bitcoin','.bitcoin')['HOME' in os.environ]
def get_from_brain_opt_params():
l,p = opt.from_brain.split(',')
return(int(l),p)
@ -301,6 +291,27 @@ def decode_pretty_hexdump(data):
msg('Data not in hexdump format')
return False
def strip_comments(line):
return re.sub(ur'\s+$',u'',re.sub(ur'#.*',u'',line,1))
def remove_comments(lines):
return [m for m in [strip_comments(l) for l in lines] if m != '']
from mmgen.globalvars import g
def start_mscolor():
if g.platform == 'win':
global red,green,yellow,cyan,nocolor
import os
if 'MMGEN_NOMSCOLOR' in os.environ:
red = green = yellow = cyan = grnbg = nocolor
else:
try:
import colorama
colorama.init(strip=True,convert=True)
except:
red = green = yellow = cyan = grnbg = nocolor
def get_hash_params(hash_preset):
if hash_preset in g.hash_presets:
return g.hash_presets[hash_preset] # N,p,r,buflen
@ -509,14 +520,6 @@ def get_words(infile,desc,prompt):
else:
return get_words_from_user(prompt)
def remove_comments(lines):
ret = []
for i in lines:
i = re.sub(ur'#.*',u'',i,1)
i = re.sub(ur'\s+$',u'',i)
if i: ret.append(i)
return ret
def mmgen_decrypt_file_maybe(fn,desc=''):
d = get_data_from_file(fn,desc,binary=True)
have_enc_ext = get_extension(fn) == g.mmenc_ext
@ -641,7 +644,7 @@ def do_license_msg(immed=False):
def get_bitcoind_cfg_options(cfg_keys):
cfg_file = os.path.join(get_homedir(), get_datadir(), 'bitcoin.conf')
cfg_file = os.path.join(g.bitcoin_data_dir,'bitcoin.conf')
cfg = dict([(k,v) for k,v in [split2(str(line).translate(None,'\t '),'=')
for line in get_lines_from_file(cfg_file,'')] if k in cfg_keys]) \
@ -653,7 +656,8 @@ def get_bitcoind_cfg_options(cfg_keys):
def get_bitcoind_auth_cookie():
f = os.path.join(get_homedir(),get_datadir(),('',g.testnet_name)[g.testnet],'.cookie')
f = os.path.join(g.bitcoin_data_dir,('',g.testnet_name)[g.testnet],
('.','_')[g.platform=='win']+'cookie')
if file_is_readable(f):
return get_lines_from_file(f,'')[0]
@ -663,7 +667,7 @@ def get_bitcoind_auth_cookie():
def bitcoin_connection():
port = (8332,18332)[g.testnet]
host,user,passwd = 'localhost','rpcuser','rpcpassword'
host,user,passwd = g.rpc_host,'rpcuser','rpcpassword'
cfg = get_bitcoind_cfg_options((user,passwd))
auth_cookie = get_bitcoind_auth_cookie()

View file

@ -9,6 +9,7 @@ from mmgen.common import *
from mmgen.tool import *
from mmgen.tx import *
from mmgen.bitcoin import *
from mmgen.obj import MMGenTXLabel
from mmgen.seed import *
from mmgen.term import do_pager
@ -24,7 +25,6 @@ import mmgen.opts
cmd_args = opts.init(help_data)
if len(cmd_args) != 1: opts.usage()
def parse_tx_file(infile):
err_str,err_fmt = '','Invalid %s in transaction file'
@ -57,9 +57,9 @@ def parse_tx_file(infile):
if comment == False:
err_str = 'encoded comment (not base58)'
else:
if is_valid_tx_comment(comment):
comment = comment.decode('utf8')
else:
try:
comment = MMGenTXLabel(comment)
except:
err_str = 'comment'
if err_str:

View file

@ -42,11 +42,11 @@ module1 = Extension(
include_dirs = ['/usr/local/include'],
)
from mmgen.globalvars import version
from mmgen.globalvars import g
setup(
name = 'mmgen',
description = 'A complete Bitcoin offline/online wallet solution for the command line',
version = version,
version = g.version,
author = 'Philemon',
author_email = 'mmgen-py@yandex.com',
url = 'https://github.com/mmgen/mmgen',
@ -56,6 +56,11 @@ setup(
cmdclass = { 'build_ext': my_build_ext },
# disable building of secp256k1 extension module on Windows
ext_modules = [module1] if sys.platform[:5] == 'linux' else [],
data_files = [('share/mmgen', [
'data_files/mmgen.cfg', # source files must have 0644 mode
'data_files/mn_wordlist.c',
'data_files/mnemonic.py'
]),],
py_modules = [
'mmgen.__init__',
'mmgen.addr',

View file

@ -319,6 +319,7 @@ cmd_group = OrderedDict()
cmd_group['help'] = OrderedDict([
# test description depends
['helpscreens', (1,'help screens', [],1)],
['longhelpscreens', (1,'help screens (--longhelp)',[],1)],
])
cmd_group['main'] = OrderedDict([
@ -505,7 +506,10 @@ meta_cmds = OrderedDict([
del cmd_group
if opt.testnet: os.environ['MMGEN_TESTNET'] = '1'
add_spawn_args = ' '.join(['{} {}'.format(
'--'+k.replace('_','-'),
getattr(opt,k) if getattr(opt,k) != True else ''
) for k in 'testnet','rpc_host' if getattr(opt,k)]).split()
if opt.profile: opt.names = True
if opt.resume: opt.skip_deps = True
@ -521,7 +525,7 @@ ni = bool(opt.non_interactive)
# Disable MS color in spawned scripts due to bad interactions
os.environ['MMGEN_NOMSCOLOR'] = '1'
os.environ['MMGEN_NOLICENSE'] = '1'
os.environ['MMGEN_NO_LICENSE'] = '1'
os.environ['MMGEN_DISABLE_COLOR'] = '1'
os.environ['MMGEN_MIN_URANDCHARS'] = '3'
@ -688,7 +692,8 @@ class MMGenExpect(object):
if type(i) not in (str,unicode):
fs = 'Error: missing input files in cmd line?:\nName: {}\nCmd: {}\nCmd args: {}'
die(2,fs.format(name,mmgen_cmd,cmd_args))
cmd_str = mmgen_cmd + ' ' + ' '.join(cmd_args)
cmd_args = add_spawn_args + cmd_args
cmd_str = '{} {}'.format(mmgen_cmd,' '.join(cmd_args))
if opt.log:
log_fd.write(cmd_str+'\n')
if opt.verbose or opt.exact_output:
@ -713,7 +718,7 @@ class MMGenExpect(object):
if opt.exact_output: self.p.logfile = sys.stdout
def license(self):
if 'MMGEN_NOLICENSE' in os.environ: return
if 'MMGEN_NO_LICENSE' in os.environ: return
p = "'w' for conditions and warranty info, or 'c' to continue: "
my_expect(self.p,p,'c')
@ -1059,13 +1064,15 @@ class MMGenTestSuite(object):
def generate_cmd_deps(self,fdeps):
return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps]
def helpscreens(self,name):
def helpscreens(self,name,arg='--help'):
for s in scripts:
t = MMGenExpect(name,('mmgen-'+s),['--help'],
t = MMGenExpect(name,('mmgen-'+s),[arg],
extra_desc='(mmgen-%s)'%s,no_output=True)
if not ni:
t.read(); ok()
def longhelpscreens(self,name): self.helpscreens(name,arg='--longhelp')
def walletgen(self,name,seed_len=None):
write_to_tmpfile(cfg,pwfile,cfg['wpasswd']+'\n')
add_args = ([usr_rand_arg],
@ -1406,7 +1413,7 @@ class MMGenTestSuite(object):
args=['-H','%s,%s'%(rf,hincog_offset),'-l',str(hincog_seedlen)])
def keyaddrgen(self,name,wf,pf=None,check_ref=False):
args = ['-d',cfg['tmpdir'],wf,cfg['addr_idx_list']]
args = ['-d',cfg['tmpdir'],usr_rand_arg,wf,cfg['addr_idx_list']]
if ni:
m = "\nAnswer 'n' at the interactive prompt"
msg(grnbg(m))
@ -1420,6 +1427,7 @@ class MMGenTestSuite(object):
refcheck('key-address data checksum',chk,cfg['keyaddrfile_chk'])
return
t.expect('Encrypt key list? (y/N): ','y')
t.usr_rand(usr_rand_chars)
t.hash_preset('new key list','1')
# t.passphrase_new('new key list','kafile password')
t.passphrase_new('new key list',cfg['kapasswd'])
@ -1531,8 +1539,9 @@ class MMGenTestSuite(object):
app = ['hash_preset=1']
else:
pre,app = [],[]
t = MMGenExpect(name,'mmgen-tool',pre+['-d',cfg['tmpdir'],'encrypt',infn]+app)
t = MMGenExpect(name,'mmgen-tool',pre+['-d',cfg['tmpdir'],usr_rand_arg,'encrypt',infn]+app)
if ni: return
t.usr_rand(usr_rand_chars)
t.hash_preset('user data','1')
t.passphrase_new('user data',tool_enc_passwd)
t.written_to_file('Encrypted data')

View file

@ -113,10 +113,10 @@ opts_data = {
'usage':'[options] [command]',
'options': """
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-l, --list-cmds List and describe the tests and commands in the test suite
-s, --system Test scripts and modules installed on system rather than
those in the repo root
--, --testnet Run on testnet rather than mainnet
-v, --verbose Produce more verbose output
""",
'notes': """
@ -126,7 +126,10 @@ If no command is given, the whole suite of tests is run.
}
cmd_args = opts.init(opts_data,add_opts=['exact_output','profile'])
if opt.testnet: os.environ['MMGEN_TESTNET'] = '1'
add_spawn_args = ' '.join(['{} {}'.format(
'--'+k.replace('_','-'),
getattr(opt,k) if getattr(opt,k) != True else ''
) for k in 'testnet','rpc_host' if getattr(opt,k)]).split()
if opt.system: sys.path.pop(0)
@ -187,7 +190,7 @@ class MMGenToolTestSuite(object):
if not opt.system:
mmgen_tool = os.path.join(os.curdir,mmgen_tool)
sys_cmd = ['python', mmgen_tool, '-d',cfg['tmpdir'], name] + tool_args + kwargs.split()
sys_cmd = ['python',mmgen_tool] + add_spawn_args + ['-r0','-d',cfg['tmpdir'],name] + tool_args + kwargs.split()
if extra_msg: extra_msg = '(%s)' % extra_msg
full_name = ' '.join([name]+kwargs.split()+extra_msg.split())
if not silent: