Browse Source

py3port: remaining changes to modules

MMGen 6 years ago
parent
commit
9c4d7d725f
14 changed files with 87 additions and 79 deletions
  1. 2 2
      mmgen/addr.py
  2. 1 3
      mmgen/altcoin.py
  3. 2 1
      mmgen/globalvars.py
  4. 2 0
      mmgen/main.py
  5. 2 1
      mmgen/main_autosign.py
  6. 16 13
      mmgen/obj.py
  7. 1 3
      mmgen/opts.py
  8. 2 3
      mmgen/protocol.py
  9. 1 1
      mmgen/rpc.py
  10. 7 8
      mmgen/seed.py
  11. 2 2
      mmgen/share/Opts.py
  12. 12 9
      mmgen/tool.py
  13. 29 26
      mmgen/tx.py
  14. 8 7
      mmgen/util.py

+ 2 - 2
mmgen/addr.py

@@ -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

+ 1 - 3
mmgen/altcoin.py

@@ -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',),

+ 2 - 1
mmgen/globalvars.py

@@ -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

+ 2 - 0
mmgen/main.py

@@ -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)

+ 2 - 1
mmgen/main_autosign.py

@@ -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:

+ 16 - 13
mmgen/obj.py

@@ -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

+ 1 - 3
mmgen/opts.py

@@ -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

+ 2 - 3
mmgen/protocol.py

@@ -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 {

+ 1 - 1
mmgen/rpc.py

@@ -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)]

+ 7 - 8
mmgen/seed.py

@@ -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)

+ 2 - 2
mmgen/share/Opts.py

@@ -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

+ 12 - 9
mmgen/tool.py

@@ -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')

+ 29 - 26
mmgen/tx.py

@@ -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))

+ 8 - 7
mmgen/util.py

@@ -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))