py3port: remaining changes to modules

This commit is contained in:
The MMGen Project 2018-10-31 18:17:58 +00:00
commit 9c4d7d725f
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
14 changed files with 87 additions and 79 deletions

View file

@ -857,8 +857,8 @@ Record this checksum: it will be used to verify the password file in the future
return True
def scramble_seed(self,seed):
# Changing either pw_fmt, pw_len or scramble_key will cause a different,
# unrelated set of passwords to be generated: this is what we want.
# Changing either pw_fmt or pw_len will cause a different, unrelated
# set of passwords to be generated: this is what we want.
# NB: In original implementation, pw_id_str was 'baseN', not 'bN'
scramble_key = '{}:{}:{}'.format(self.pw_fmt,self.pw_len,self.pw_id_str)
from mmgen.crypto import scramble_seed

View file

@ -432,8 +432,6 @@ class CoinInfo(object):
line[n] = re.sub(r' ',r'',line[n]) + ('',',')[n != len(line)-1]
from mmgen.util import pmsg,pdie
# pmsg(sym)
# pdie(tt)
if trust != -1:
if sym in tt:
src = tt[sym]
@ -501,7 +499,7 @@ class CoinInfo(object):
'mainnet': {
'pycoin': (
# broken: DASH - only compressed, LTC segwit old fmt
'BTC','LTC','VIA','FTC','DOGE','MEC','MYR','UNO',
'BTC','LTC','VIA','FTC','DOGE','MEC',
'JBS','MZC','RIC','DFC','FAI','ARG','ZEC','DCR'),
'pyethereum': ('ETH','ETC'),
'zcash_mini': ('ZEC',),

View file

@ -122,7 +122,8 @@ class g(object):
required_opts = (
'quiet','verbose','debug','outdir','echo_passphrase','passwd_file','stdout',
'show_hash_presets','label','keep_passphrase','keep_hash_preset','yes',
'brain_params','b16','usr_randchars','coin','bob','alice','key_generator'
'brain_params','b16','usr_randchars','coin','bob','alice','key_generator',
'hidden_incog_input_params','in_fmt'
)
incompatible_opts = (
('base32','hex'), # mmgen-passgen

View file

@ -26,6 +26,8 @@ def launch(what):
what = 'wallet'
if what == 'keygen': what = 'addrgen'
import sys
try: import termios
except: # Windows
__import__('mmgen.main_' + what)

View file

@ -161,6 +161,7 @@ def do_umount():
subprocess.call(['umount',mountpoint])
def sign_tx_file(txfile):
from importlib import reload
try:
g.testnet = False
g.coin = 'BTC'
@ -360,7 +361,7 @@ def do_loop():
while True:
status = get_insert_status()
if status and not prev_status:
msg('Device insert detected')
msg('Device insertion detected')
do_sign()
prev_status = status
if not n % 10:

View file

@ -139,13 +139,14 @@ class Hilite(object):
color_always = False
width = 0
trunc_ok = True
dtype = str
@classmethod
# 'width' is screen width (greater than len(s) for CJK strings)
# 'append_chars' and 'encl' must consist of single-width chars only
def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None,
center=False,nullrepl='',append_chars='',append_color=False):
s = str(s)
if cls.dtype == bytes: s = s.decode()
s_wide_count = len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F','W')])
assert type(encl) is str and len(encl) in (0,2),"'encl' must be 2-character str"
a,b = list(encl) if encl else ('','')
@ -168,6 +169,12 @@ class Hilite(object):
else:
return cls.colorize(s.ljust(width-s_wide_count),color=color)
@classmethod
def colorize(cls,s,color=True):
if cls.dtype == bytes: s = s.decode()
k = color if type(color) is str else cls.color # hack: override color with str value
return globals()[k](s) if (color or cls.color_always) else s
def fmt(self,*args,**kwargs):
assert args == () # forbid invocation w/o keywords
return self.fmtc(self,*args,**kwargs)
@ -182,11 +189,6 @@ class Hilite(object):
def __str__(self):
return self.colorize(self,color=False)
@classmethod
def colorize(cls,s,color=True):
k = color if type(color) is str else cls.color # hack: override color with str value
return globals()[k](s) if (color or cls.color_always) else s
# For attrs that are always present in the data instance
# Reassignment and deletion forbidden
class MMGenImmutableAttr(object): # Descriptor
@ -202,7 +204,8 @@ class MMGenImmutableAttr(object): # Descriptor
# forbid all reassignment
def set_attr_ok(self,instance):
return not hasattr(instance,self.name)
return not self.name in instance.__dict__
# return not hasattr(instance,self.name)
def __set__(self,instance,value):
if not self.set_attr_ok(instance):
@ -624,7 +627,7 @@ class WifKey(str,Hilite,InitErrors):
if type(s) == cls: return s
cls.arg_chk(cls,on_fail)
try:
assert set(s) <= set(ascii_letters+digits),'not an ascii string'
assert set(s) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
from mmgen.globalvars import g
g.proto.wif2hex(s) # raises exception on error
return str.__new__(cls,s)
@ -643,7 +646,7 @@ class PubKey(HexBytes,MMGenObject): # TODO: add some real checks
m = '{!r}: invalid value for pubkey ({})'.format(s,e.args[0])
return cls.init_fail(m,on_fail)
class PrivKey(str,Hilite,InitErrors,MMGenObject):
class PrivKey(bytes,Hilite,InitErrors,MMGenObject):
color = 'red'
width = 64
@ -662,9 +665,9 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
if wif:
try:
assert s == None
assert set(wif) <= set(ascii_letters+digits),'not an ascii string'
assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
w2h = g.proto.wif2hex(wif) # raises exception on error
me = str.__new__(cls,w2h['hex'])
me = bytes.__new__(cls,w2h['hex'])
me.compressed = w2h['compressed']
me.pubkey_type = w2h['pubkey_type']
me.wif = str.__new__(WifKey,wif) # check has been done
@ -678,9 +681,9 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
assert s and type(compressed) == bool and pubkey_type,'Incorrect args for PrivKey()'
assert len(s) == cls.width // 2,'Key length must be {}'.format(cls.width/2)
if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
me = str.__new__(cls,hexlify(s))
me = bytes.__new__(cls,hexlify(s))
else:
me = str.__new__(cls,g.proto.preprocess_key(hexlify(s),pubkey_type))
me = bytes.__new__(cls,g.proto.preprocess_key(hexlify(s),pubkey_type))
me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise')
me.compressed = compressed
me.pubkey_type = pubkey_type

View file

@ -20,7 +20,7 @@
"""
opts.py: MMGen-specific options processing after generic processing by share.Opts
"""
import sys,os
import sys,os,stat
class opt(object): pass
@ -161,10 +161,8 @@ def override_from_cfg_file(cfg_data):
else:
cls,attr = g,name
setattr(cls,attr,set_for_type(val,getattr(cls,attr),attr,src=g.cfg_file))
# pmsg(cls,attr,getattr(cls,attr))
else:
die(2,"'{}': unrecognized option in '{}'".format(name,g.cfg_file))
# pdie('xxx')
def override_from_env():
from mmgen.util import set_for_type

View file

@ -122,7 +122,7 @@ class BitcoinProtocol(MMGenObject):
return '{:064x}'.format(pk % cls.secp256k1_ge).encode()
@classmethod
def hex2wif(cls,hexpriv,pubkey_type,compressed):
def hex2wif(cls,hexpriv,pubkey_type,compressed): # PrivKey
return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + (b'',b'01')[bool(compressed)])
@classmethod
@ -153,7 +153,7 @@ class BitcoinProtocol(MMGenObject):
msg('{}: Invalid witness version number'.format(ret[0]))
elif ret[1]:
return {
'hex': hexlify(''.join(map(chr,ret[1]))),
'hex': hexlify(bytes(ret[1])),
'format': 'bech32'
} if return_dict else True
return False
@ -168,7 +168,6 @@ class BitcoinProtocol(MMGenObject):
if g.debug: Msg('Address cannot be converted to base 58')
break
addr_hex = '{:0{}x}'.format(num,len(ver_num)+hex_width+8).encode()
# pmsg(hex_width,len(addr_hex),addr_hex[:len(ver_num)],ver_num)
if addr_hex[:len(ver_num)] != ver_num: continue
if hash256(addr_hex[:-8])[:8] == addr_hex[-8:]:
return {

View file

@ -91,7 +91,7 @@ class CoinDaemonRPCConnection(object):
for k in cf:
if k in kwargs and kwargs[k]: cf[k] = kwargs[k]
hc = http.client.HTTPConnection(self.host, self.port, False, cf['timeout'])
hc = http.client.HTTPConnection(self.host,self.port,cf['timeout'])
if cf['batch']:
p = [{'method':cmd,'params':r,'id':n,'jsonrpc':'2.0'} for n,r in enumerate(args[0],1)]

View file

@ -196,7 +196,7 @@ class SeedSource(MMGenObject):
@classmethod
def format_fmt_codes(cls):
d = [(c.__name__,('.'+c.ext if c.ext else c.ext),','.join(c.fmt_codes))
d = [(c.__name__,('.'+c.ext if c.ext else str(c.ext)),','.join(c.fmt_codes))
for c in cls.get_subclasses()
if hasattr(c,'fmt_codes')]
w = max(len(i[0]) for i in d)
@ -414,16 +414,15 @@ class Mnemonic (SeedSourceUnenc):
idx = bisect_left(wl,w)
return(True,False)[idx == len(wl) or w != wl[idx]]
words,i,p = [],0,('Enter word #{}: ','Incorrect entry. Repeat word #{}: ')
p = ('Enter word #{}: ','Incorrect entry. Repeat word #{}: ')
words,err = [],0
while len(words) < mn_len:
msg_r('{r}{s}{r}'.format(r='\r',s=' '*40))
if i == 1: time.sleep(0.1)
msg_r(p[i].format(len(words)+1))
if err == 1: time.sleep(0.1)
msg_r(p[err].format(len(words)+1))
s = get_word()
if in_list(s):
words.append(s); i = 0
else:
i = 1
if in_list(s): words.append(s)
err = (1,0)[in_list(s)]
msg('')
qmsg('Mnemonic successfully entered')
return ' '.join(words)

View file

@ -22,7 +22,6 @@ Opts.py: Generic options handling
import sys,getopt
import collections
# from mmgen.util import mdie,die,pdie,pmsg # DEBUG
def usage(opts_data):
print(('USAGE: {} {}'.format(opts_data['prog_name'], opts_data['usage'])))
@ -56,7 +55,8 @@ def process_opts(argv,opts_data,short_opts,long_opts,skip_help=False):
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)
print(str(err))
sys.exit(2)
sopts_list = ':_'.join(['_'.join(list(i)) for i in short_opts.split(':')]).split('_')
opts,skipped_help = {},False

View file

@ -22,17 +22,16 @@ tool.py: Routines and data for the 'mmgen-tool' utility
"""
import binascii
from collections import OrderedDict
from mmgen.protocol import hash160
from mmgen.common import *
from mmgen.crypto import *
from mmgen.tx import *
from mmgen.addr import *
from decimal import Decimal
pnm = g.proj_name
from collections import OrderedDict
cmd_data = OrderedDict([
('Help', ['<tool command> [str]']),
('Usage', ['<tool command> [str]']),
@ -194,7 +193,7 @@ def process_args(command,cmd_args):
usage(command)
def conv_type(arg,arg_name,arg_type):
if arg_type == 'str': arg_type = 'unicode'
if arg_type == 'bytes': pdie(arg,arg_name,arg_type)
if arg_type == 'bool':
if arg.lower() in ('true','yes','1','on'): arg = True
elif arg.lower() in ('false','no','0','off'): arg = False
@ -246,8 +245,9 @@ def Unhexdump(infile):
if g.platform == 'win':
import msvcrt
msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
sys.stdout.write(decode_pretty_hexdump(
get_data_from_file(infile,dash=True,silent=True)))
hexdata = get_data_from_file(infile,dash=True,silent=True)
ret = decode_pretty_hexdump(hexdata)
os.write(g.stdout_fileno,ret)
def B58randenc():
r = get_random(32)
@ -594,7 +594,8 @@ def monero_wallet_ops(infile,op,blockheight=None,addrs=None):
msg(' Wallet in sync')
b = [l for l in p.before.decode().splitlines() if len(l) > 7 and l[:8] == 'Balance:'][0].split()
msg(' Balance: {} Unlocked balance: {}'.format(b[1],b[4]))
bals[fn] = ( Decimal(b[1][:-1]), Decimal(b[4]) )
from mmgen.obj import XMRAmt
bals[fn] = ( XMRAmt(b[1][:-1]), XMRAmt(b[4]) )
my_sendline(p,'Exiting','exit',5)
p.read()
break
@ -626,8 +627,8 @@ def monero_wallet_ops(infile,op,blockheight=None,addrs=None):
col1_w = max(list(map(len,bals))) + 1
fs = '{:%s} {} {}' % col1_w
msg('\n'+fs.format('Wallet','Balance ','Unlocked Balance '))
tbals = [Decimal('0'),Decimal('0')]
from mmgen.obj import XMRAmt
tbals = [XMRAmt('0'),XMRAmt('0')]
for bal in bals:
for i in (0,1): tbals[i] += bals[bal][i]
msg(fs.format(bal+':',*[XMRAmt(b).fmt(fs='5.12',color=True) for b in bals[bal]]))
@ -655,7 +656,7 @@ def monero_wallet_ops(infile,op,blockheight=None,addrs=None):
# ================ RPC commands ================== #
def Gen_addr(addr,wallet='',target='addr'):
def Gen_addr(addr,wallet='',target='addr',return_result=False):
addr = MMGenID(addr)
sf = get_seed_file([wallet] if wallet else [],1)
opt.quiet = True
@ -666,7 +667,9 @@ def Gen_addr(addr,wallet='',target='addr'):
die(1,m.format(addr.sid,ss.seed.sid))
al = AddrList(seed=ss.seed,addr_idxs=AddrIdxList(str(addr.idx)),mmtype=addr.mmtype,do_chksum=False)
d = al.data[0]
Msg(d.sec.wif if target=='wif' else d.addr)
ret = d.sec.wif if target=='wif' else d.addr
if return_result: return ret
else: Msg(ret)
def Gen_key(addr,wallet=''):
return Gen_addr(addr,wallet,target='wif')

View file

@ -55,12 +55,12 @@ def strfmt_locktime(num,terse=False):
# If greater than or equal to 500 million, locktime is parsed using the Unix epoch time
# format (the number of seconds elapsed since 1970-01-01T00:00 UTC). The transaction can be
# added to any block whose block time is greater than the locktime.
if num >= 5 * 10**6:
if num == None:
return '(None)'
elif num >= 5 * 10**6:
return ' '.join(time.strftime('%c',time.gmtime(num)).split()[1:])
elif num > 0:
return '{}{}'.format(('block height ','')[terse],num)
elif num == None:
return '(None)'
else:
die(2,"'{}': invalid locktime value!".format(num))
@ -98,7 +98,7 @@ def segwit_is_active(exit_on_error=False):
def bytes2int(hex_bytes):
r = hexlify(unhexlify(hex_bytes)[::-1])
if r[0] in b'89abcdef':
if hexlify(bytes([r[0]])) in b'89abcdef':
die(3,"{}: Negative values not permitted in transaction!".format(hex_bytes))
return int(r,16)
@ -120,35 +120,36 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
def __init__(self,txhex):
tx = list(unhexlify(txhex))
tx_copy = tx[:]
d = { 'raw_tx':'' }
d = { 'raw_tx': [] }
def hshift(l,n,reverse=False,skip=False):
ret = l[:n]
if not skip: d['raw_tx'] += ''.join(ret)
if not skip: d['raw_tx'] += ret
del l[:n]
return hexlify(''.join(ret[::-1] if reverse else ret))
return hexlify(bytes(ret[::-1] if reverse else ret))
# https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
# For example, the number 515 is encoded as 0xfd0302.
def readVInt(l,skip=False,sub_null=False):
s = int(hexlify(l[0]),16)
s = l[0]
bytes_len = 1 if s < 0xfd else 2 if s == 0xfd else 4 if s == 0xfe else 8
if bytes_len != 1: del l[0]
ret = int(hexlify(''.join(l[:bytes_len][::-1])),16)
ret = int(hexlify(bytes(l[:bytes_len][::-1])),16)
if sub_null: d['raw_tx'] += b'\0'
elif not skip: d['raw_tx'] += ''.join(l[:bytes_len])
elif not skip: d['raw_tx'] += l[:bytes_len]
del l[:bytes_len]
return ret
d['version'] = bytes2int(hshift(tx,4))
has_witness = tx[0] == '\x00'
has_witness = tx[0] == 0
if has_witness:
u = hshift(tx,2,skip=True)[2:]
if u != '01':
u = hshift(tx,2,skip=True)
if u != b'0001':
raise IllegalWitnessFlagValue("'{}': Illegal value for flag in transaction!".format(u))
del tx_copy[-len(tx)-2:-len(tx)]
d['num_txins'] = readVInt(tx)
d['txins'] = MMGenList([OrderedDict((
('txid', hshift(tx,32,reverse=True)),
('vout', bytes2int(hshift(tx,4))),
@ -174,7 +175,7 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
wd,tx = tx[:-4],tx[-4:]
d['witness_size'] = len(wd) + 2 # add marker and flag
for i in range(len(d['txins'])):
if hexlify(wd[0]) == '00':
if wd[0] == 0:
hshift(wd,1,skip=True)
continue
d['txins'][i]['witness'] = [
@ -184,12 +185,12 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
raise WitnessSizeMismatch('More witness data than inputs with witnesses!')
d['lock_time'] = bytes2int(hshift(tx,4))
d['txid'] = hexlify(sha256(sha256(''.join(tx_copy)).digest()).digest()[::-1])
d['unsigned_hex'] = hexlify(d['raw_tx'])
d['txid'] = hexlify(sha256(sha256(bytes(tx_copy)).digest()).digest()[::-1])
d['unsigned_hex'] = hexlify(bytes(d['raw_tx']))
del d['raw_tx']
keys = 'txid','version','lock_time','witness_size','num_txins','txins','num_txouts','txouts','unsigned_hex'
return OrderedDict.__init__(self, ((k,d[k]) for k in keys))
OrderedDict.__init__(self, ((k,d[k]) for k in keys))
txio_attrs = {
'vout': MMGenListItemAttr('vout',int,typeconv=False),
@ -315,8 +316,11 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
self.outputs.append(MMGenTX.MMGenTxOutput(addr=coinaddr,amt=amt,is_chg=is_chg))
def get_chg_output_idx(self):
try: return map(lambda x: x.is_chg,self.outputs).index(True)
except ValueError: return None
ch_ops = [x.is_chg for x in self.outputs]
try:
return ch_ops.index(True)
except ValueError:
return None
def update_output_amt(self,idx,amt):
o = self.outputs[idx].__dict__
@ -352,13 +356,9 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
def check_dup_addrs(self,io_str):
assert io_str in ('inputs','outputs')
io = getattr(self,io_str)
for k in ('mmid','addr'):
old_attr = None
for attr in sorted(getattr(e,k) for e in io):
if attr != None and attr == old_attr:
die(2,'{}: duplicate address in transaction {}'.format(attr,io_str))
old_attr = attr
addrs = [e.addr for e in getattr(self,io_str)]
if len(addrs) != len(set(addrs)):
die(2,'{}: duplicate address in transaction {}'.format(attr,io_str))
def update_txid(self):
self.txid = MMGenTxID(make_chksum_6(unhexlify(self.hex)).upper())
@ -740,6 +740,9 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
msg('OK')
return True
except Exception as e:
if os.getenv('MMGEN_TRACEBACK'):
import traceback
ymsg('\n'+''.join(traceback.format_exception(*sys.exc_info())))
try: m = '{}'.format(e.args[0])
except: m = repr(e.args[0])
msg('\n'+yellow(m))

View file

@ -23,7 +23,7 @@ util.py: Low-level routines imported by other modules in the MMGen suite
import sys,os,time,stat,re,unicodedata
from hashlib import sha256
from binascii import hexlify,unhexlify
from string import hexdigits
from string import hexdigits,digits
from mmgen.color import *
from mmgen.exception import *
@ -67,7 +67,7 @@ def Die(ev=0,s=''):
def rdie(ev=0,s=''): die(ev,red(s))
def ydie(ev=0,s=''): die(ev,yellow(s))
def hi(): sys.stdout.write(yellow('hi'))
def hi(): ymsg('hi')
def pformat(d):
import pprint
@ -122,7 +122,7 @@ def check_or_create_dir(path):
os.listdir(path)
except:
try:
os.makedirs(path,0o700)
os.makedirs(path,stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR)
except:
die(2,"ERROR: unable to read or create path '{}'".format(path))
@ -232,6 +232,7 @@ def secs_to_hms(secs):
def secs_to_ms(secs):
return '{:02d}:{:02d}'.format(secs//60, secs % 60)
def is_digits(s): return set(list(s)) <= set(list(digits))
def is_int(s):
try:
int(str(s))
@ -431,7 +432,7 @@ def compare_chksums(chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False
return True
def compare_or_die(val1, desc1, val2, desc2, e='Error'):
if cmp(val1,val2):
if val1 != val2:
die(3,"{}: {} ({}) doesn't match {} ({})".format(e,desc2,val2,desc1,val1))
dmsg('{} OK ({})'.format(capfirst(desc2),val2))
return True
@ -740,8 +741,8 @@ def keypress_confirm(prompt,default_yes=False,verbose=False,no_nl=False):
return (False,True)[default_yes]
while True:
reply = get_char(p).strip(b'\n\r')
if not reply:
r = get_char(p).strip(b'\n\r')
if not r:
if default_yes: msg_r(nl); return True
else: msg_r(nl); return False
elif r in b'yY': msg_r(nl); return True
@ -812,7 +813,7 @@ def get_daemon_cfg_options(cfg_keys):
cfg_file = os.path.join(g.proto.daemon_data_dir,g.proto.name+'.conf')
try:
lines = get_lines_from_file(cfg_file,'',silent=bool(opt.quiet))
kv_pairs = [split2(str(line).translate(None,'\t '),'=') for line in lines]
kv_pairs = [l.split('=') for l in lines]
cfg = dict([(k,v) for k,v in kv_pairs if k in cfg_keys])
except:
vmsg("Warning: '{}' does not exist or is unreadable".format(cfg_file))