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:
parent
3897ec3297
commit
456cc1f76c
29 changed files with 507 additions and 328 deletions
43
data_files/mmgen.cfg
Normal file
43
data_files/mmgen.cfg
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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 = \
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': """
|
||||
|
|
|
|||
|
|
@ -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': """
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:')
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
9
setup.py
9
setup.py
|
|
@ -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',
|
||||
|
|
|
|||
25
test/test.py
25
test/test.py
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue