diff --git a/mmgen/addr.py b/mmgen/addr.py index 4dbb9aae..ae59fc7e 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -21,7 +21,6 @@ addr.py: Address generation/display routines for the MMGen suite """ from hashlib import sha256,sha512 -from binascii import hexlify,unhexlify from mmgen.common import * from mmgen.obj import * @@ -67,7 +66,7 @@ class AddrGeneratorSegwit(AddrGenerator): def to_segwit_redeem_script(self,pubhex): assert pubhex.compressed,'Uncompressed public keys incompatible with Segwit' - return HexBytes(g.proto.pubhex2redeem_script(pubhex)) + return HexStr(g.proto.pubhex2redeem_script(pubhex)) class AddrGeneratorBech32(AddrGenerator): def to_addr(self,pubhex): @@ -82,7 +81,7 @@ class AddrGeneratorEthereum(AddrGenerator): def to_addr(self,pubhex): assert type(pubhex) == PubKey import sha3 - return CoinAddr(hexlify(sha3.keccak_256(unhexlify(pubhex[2:])).digest()[12:]).decode()) + return CoinAddr(sha3.keccak_256(bytes.fromhex(pubhex[2:])).hexdigest()[24:]) def to_wallet_passwd(self,sk_hex): from mmgen.protocol import hash256 @@ -105,26 +104,26 @@ class AddrGeneratorZcashZ(AddrGenerator): return Sha256(s,preprocess=False).digest() def to_addr(self,pubhex): # pubhex is really privhex - key = unhexlify(pubhex) + key = bytes.fromhex(pubhex) assert len(key) == 32,'{}: incorrect privkey length'.format(len(key)) if g.platform == 'win': ydie(1,'Zcash z-addresses not supported on Windows platform') from nacl.bindings import crypto_scalarmult_base p2 = crypto_scalarmult_base(self.zhash256(key,1)) from mmgen.protocol import _b58chk_encode - ret = _b58chk_encode(g.proto.addr_ver_num['zcash_z'][0] + hexlify(self.zhash256(key,0)+p2)) + ret = _b58chk_encode(g.proto.addr_ver_num['zcash_z'][0] + (self.zhash256(key,0)+p2).hex()) assert len(ret) == self.addr_width,'Invalid Zcash z-address length' return CoinAddr(ret) def to_viewkey(self,pubhex): # pubhex is really privhex - key = unhexlify(pubhex) + key = bytes.fromhex(pubhex) assert len(key) == 32,'{}: incorrect privkey length'.format(len(key)) vk = bytearray(self.zhash256(key,0)+self.zhash256(key,1)) vk[32] &= 0xf8 vk[63] &= 0x7f vk[63] |= 0x40 from mmgen.protocol import _b58chk_encode - ret = _b58chk_encode(g.proto.addr_ver_num['viewkey'][0] + hexlify(vk)) + ret = _b58chk_encode(g.proto.addr_ver_num['viewkey'][0] + vk.hex()) assert len(ret) == self.vk_width,'Invalid Zcash view key length' return ZcashViewKey(ret) @@ -133,10 +132,11 @@ class AddrGeneratorZcashZ(AddrGenerator): class AddrGeneratorMonero(AddrGenerator): - def b58enc(self,addr_str): - enc,l = baseconv.fromhex,len(addr_str) - a = ''.join([enc(hexlify(addr_str[i*8:i*8+8]),'b58',pad=11,tostr=True) for i in range(l//8)]) - b = enc(hexlify(addr_str[l-l%8:]),'b58',pad=7,tostr=True) + def b58enc(self,addr_bytes): + enc = baseconv.fromhex + l = len(addr_bytes) + a = ''.join([enc((addr_bytes[i*8:i*8+8]).hex(),'b58',pad=11,tostr=True) for i in range(l//8)]) + b = enc((addr_bytes[l-l%8:]).hex(),'b58',pad=7,tostr=True) return a + b def to_addr(self,sk_hex): # sk_hex instead of pubhex @@ -159,12 +159,12 @@ class AddrGeneratorMonero(AddrGenerator): return Q def hex2int_le(hexstr): - return int(hexlify(unhexlify(hexstr)[::-1]),16) + return int((bytes.fromhex(hexstr)[::-1]).hex(),16) vk_hex = self.to_viewkey(sk_hex) pk_str = encodepoint(scalarmultbase(hex2int_le(sk_hex))) pvk_str = encodepoint(scalarmultbase(hex2int_le(vk_hex))) - addr_p1 = unhexlify(g.proto.addr_ver_num['monero'][0]) + pk_str + pvk_str + addr_p1 = bytes.fromhex(g.proto.addr_ver_num['monero'][0]) + pk_str + pvk_str import sha3 return CoinAddr(self.b58enc(addr_p1 + sha3.keccak_256(addr_p1).digest()[:4])) @@ -176,7 +176,7 @@ class AddrGeneratorMonero(AddrGenerator): def to_viewkey(self,sk_hex): assert len(sk_hex) == 64,'{}: incorrect privkey length'.format(len(sk_hex)) import sha3 - return MoneroViewKey(g.proto.preprocess_key(sha3.keccak_256(unhexlify(sk_hex)).hexdigest(),None)) + return MoneroViewKey(g.proto.preprocess_key(sha3.keccak_256(bytes.fromhex(sk_hex)).hexdigest(),None)) def to_segwit_redeem_script(self,sk_hex): raise NotImplementedError('Monero addresses incompatible with Segwit') @@ -210,7 +210,7 @@ class KeyGenerator(MMGenObject): try: from mmgen.secp256k1 import priv2pub m = 'Unable to execute priv2pub() from secp256k1 extension module' - assert priv2pub(unhexlify('deadbeef'*8),1),m + assert priv2pub(bytes.fromhex('deadbeef'*8),1),m return True except: return False @@ -239,12 +239,12 @@ class KeyGeneratorPython(KeyGenerator): def privnum2pubhex(self,numpriv,compressed=False): pko = ecdsa.SigningKey.from_secret_exponent(numpriv,self.secp256k1) # pubkey = x (32 bytes) + y (32 bytes) (unsigned big-endian) - pubkey = hexlify(pko.get_verifying_key().to_string()) + pubkey = (pko.get_verifying_key().to_string()).hex() if compressed: # discard Y coord, replace with appropriate version byte # even y: <0, odd y: >0 -- https://bitcointalk.org/index.php?topic=129652.0 - return (b'03',b'02')[pubkey[-1] in b'02468ace'] + pubkey[:64] + return ('03','02')[pubkey[-1] in '02468ace'] + pubkey[:64] else: - return b'04' + pubkey + return '04' + pubkey def to_pubhex(self,privhex): assert type(privhex) == PrivKey @@ -256,13 +256,13 @@ class KeyGeneratorSecp256k1(KeyGenerator): def to_pubhex(self,privhex): assert type(privhex) == PrivKey from mmgen.secp256k1 import priv2pub - return PubKey(hexlify(priv2pub(unhexlify(privhex),int(privhex.compressed))),compressed=privhex.compressed) + return PubKey(priv2pub(bytes.fromhex(privhex),int(privhex.compressed)).hex(),compressed=privhex.compressed) class KeyGeneratorDummy(KeyGenerator): desc = 'mmgen-dummy' def to_pubhex(self,privhex): assert type(privhex) == PrivKey - return PubKey(privhex.decode(),compressed=privhex.compressed) + return PubKey(privhex,compressed=privhex.compressed) class AddrListEntry(MMGenListItem): addr = MMGenListItemAttr('addr','CoinAddr') @@ -404,7 +404,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file seed = seed.get_data() seed = self.scramble_seed(seed) - dmsg_sc('seed',hexlify(seed[:8]).decode()) + dmsg_sc('seed',seed[:8].hex()) compressed = self.al_id.mmtype.compressed pubkey_type = self.al_id.mmtype.pubkey_type @@ -838,7 +838,7 @@ Record this checksum: it will be used to verify the password file in the future def make_passwd(self,hex_sec): assert self.pw_fmt in self.pw_info if self.pw_fmt == 'hex': - return hex_sec.decode() + return hex_sec else: # we take least significant part return baseconv.fromhex(hex_sec,self.pw_fmt,pad=self.pw_len,tostr=True)[-self.pw_len:] diff --git a/mmgen/altcoins/eth/contract.py b/mmgen/altcoins/eth/contract.py index 6fd20a6b..88f26b54 100755 --- a/mmgen/altcoins/eth/contract.py +++ b/mmgen/altcoins/eth/contract.py @@ -70,8 +70,8 @@ class Token(MMGenObject): # ERC20 except: "RPC call to decimals() failed (returned '{}')".format(ret) return int(b,16) if b else None - def name(self): return self.strip(unhexlify(self.do_call('name()')[2:])) - def symbol(self): return self.strip(unhexlify(self.do_call('symbol()')[2:])) + def name(self): return self.strip(bytes.fromhex(self.do_call('name()')[2:])) + def symbol(self): return self.strip(bytes.fromhex(self.do_call('symbol()')[2:])) def info(self): fs = '{:15}{}\n' * 5 @@ -95,12 +95,12 @@ class Token(MMGenObject): # ERC20 if nonce is None: nonce = int(g.rpch.parity_nextNonce('0x'+from_addr),16) data = self.create_data(to_addr,amt,method_sig=method_sig,from_addr=from_addr2) - return {'to': unhexlify(self.addr), + return {'to': bytes.fromhex(self.addr), 'startgas': start_gas.toWei(), 'gasprice': gasPrice.toWei(), 'value': 0, 'nonce': nonce, - 'data': unhexlify(data) } + 'data': bytes.fromhex(data) } def txsign(self,tx_in,key,from_addr,chain_id=None): from ethereum.transactions import Transaction @@ -108,11 +108,11 @@ class Token(MMGenObject): # ERC20 chain_id_method = ('parity_chainId','eth_chainId')['eth_chainId' in g.rpch.caps] chain_id = int(g.rpch.request(chain_id_method),16) tx = Transaction(**tx_in).sign(key,chain_id) - hex_tx = hexlify(rlp.encode(tx)) - coin_txid = CoinTxID(hexlify(tx.hash)) - if hexlify(tx.sender).decode() != from_addr: + hex_tx = rlp.encode(tx).hex() + coin_txid = CoinTxID(tx.hash.hex()) + if tx.sender.hex() != from_addr: m = "Sender address '{}' does not match address of key '{}'!" - die(3,m.format(from_addr,hexlify(tx.sender).decode())) + die(3,m.format(from_addr,tx.sender.hex())) if g.debug: msg('{}'.format('\n '.join(parse_abi(data)))) pmsg(tx.to_dict()) @@ -121,7 +121,7 @@ class Token(MMGenObject): # ERC20 # The following are used for token deployment only: def txsend(self,hex_tx): - return g.rpch.eth_sendRawTransaction('0x'+hex_tx.decode()).replace('0x','',1).encode() + return g.rpch.eth_sendRawTransaction('0x'+hex_tx).replace('0x','',1) def transfer( self,from_addr,to_addr,amt,key,start_gas,gasPrice, method_sig='transfer(address,uint256)', diff --git a/mmgen/altcoins/eth/tx.py b/mmgen/altcoins/eth/tx.py index 0dc6fc33..80b698b7 100755 --- a/mmgen/altcoins/eth/tx.py +++ b/mmgen/altcoins/eth/tx.py @@ -44,7 +44,7 @@ class EthereumMMGenTX(MMGenTX): usr_rel_fee = None # not in MMGenTX disable_fee_check = False txobj = None # "" - data = HexBytes('') + data = HexStr('') def __init__(self,*args,**kwargs): super(EthereumMMGenTX,self).__init__(*args,**kwargs) @@ -53,16 +53,16 @@ class EthereumMMGenTX(MMGenTX): if hasattr(opt,'contract_data') and opt.contract_data: m = "'--contract-data' option may not be used with token transaction" assert not 'Token' in type(self).__name__, m - self.data = HexBytes(open(opt.contract_data).read().strip()) + self.data = HexStr(open(opt.contract_data).read().strip()) self.disable_fee_check = True @classmethod def get_receipt(cls,txid): - return g.rpch.eth_getTransactionReceipt('0x'+txid.decode()) + return g.rpch.eth_getTransactionReceipt('0x'+txid) @classmethod def get_exec_status(cls,txid,silent=False): - d = g.rpch.eth_getTransactionReceipt('0x'+txid.decode()) + d = g.rpch.eth_getTransactionReceipt('0x'+txid) if not silent: if 'contractAddress' in d and d['contractAddress']: msg('Contract address: {}'.format(d['contractAddress'].replace('0x',''))) @@ -82,18 +82,17 @@ class EthereumMMGenTX(MMGenTX): def check_pubkey_scripts(self): pass def check_sigs(self,deserial_tx=None): - if is_hex_bytes(self.hex): + if is_hex_str(self.hex): self.mark_signed() return True return False # hex data if signed, json if unsigned: see create_raw() def check_txfile_hex_data(self): - if type(self.hex) == str: self.hex = self.hex.encode() if self.check_sigs(): from ethereum.transactions import Transaction import rlp - etx = rlp.decode(unhexlify(self.hex),Transaction) + etx = rlp.decode(bytes.fromhex(self.hex),Transaction) d = etx.to_dict() # ==> hex values have '0x' prefix, 0 is '0x' for k in ('sender','to','data'): if k in d: d[k] = d[k].replace('0x','',1) @@ -103,13 +102,13 @@ class EthereumMMGenTX(MMGenTX): 'gasPrice': ETHAmt(d['gasprice'],'wei'), 'startGas': ETHAmt(d['startgas'],'wei'), 'nonce': ETHNonce(d['nonce']), - 'data': HexBytes(d['data']) } + 'data': HexStr(d['data']) } if o['data'] and not o['to']: - self.token_addr = TokenAddr(hexlify(etx.creates).decode()) - txid = CoinTxID(hexlify(etx.hash)) + self.token_addr = TokenAddr(etx.creates.hex()) + txid = CoinTxID(etx.hash.hex()) assert txid == self.coin_txid,"txid in tx.hex doesn't match value in MMGen transaction file" else: - d = json.loads(self.hex.decode()) + d = json.loads(self.hex) o = { 'from': CoinAddr(d['from']), 'to': CoinAddr(d['to']) if d['to'] else Str(''), 'amt': ETHAmt(d['amt']), @@ -117,7 +116,7 @@ class EthereumMMGenTX(MMGenTX): 'startGas': ETHAmt(d['startGas']), 'nonce': ETHNonce(d['nonce']), 'chainId': Int(d['chainId']), - 'data': HexBytes(d['data']) } + 'data': HexStr(d['data']) } self.tx_gas = o['startGas'] # approximate, but better than nothing self.data = o['data'] if o['data'] and not o['to']: self.disable_fee_check = True @@ -151,7 +150,7 @@ class EthereumMMGenTX(MMGenTX): assert o_num in o_ok,'Transaction has invalid number of outputs!'.format(o_num) self.make_txobj() ol = {k: (v.decode() if issubclass(type(v),bytes) else str(v)) for k,v in self.txobj.items()} - self.hex = json.dumps(ol).encode() + self.hex = json.dumps(ol) self.update_txid() def del_output(self,idx): pass @@ -278,22 +277,22 @@ class EthereumMMGenTX(MMGenTX): return m.format(ETHAmt(chg).hl(),g.coin) def do_sign(self,d,wif,tx_num_str): - d_in = {'to': unhexlify(d['to']), + d_in = {'to': bytes.fromhex(d['to']), 'startgas': d['startGas'].toWei(), 'gasprice': d['gasPrice'].toWei(), 'value': d['amt'].toWei() if d['amt'] else 0, 'nonce': d['nonce'], - 'data': unhexlify(d['data'])} + 'data': bytes.fromhex(d['data'])} from ethereum.transactions import Transaction etx = Transaction(**d_in).sign(wif,d['chainId']) - assert hexlify(etx.sender).decode() == d['from'],( + assert etx.sender.hex() == d['from'],( 'Sender address recovered from signature does not match true sender') import rlp - self.hex = hexlify(rlp.encode(etx)) - self.coin_txid = CoinTxID(hexlify(etx.hash)) + self.hex = rlp.encode(etx).hex() + self.coin_txid = CoinTxID(etx.hash.hex()) if d['data']: - self.token_addr = TokenAddr(hexlify(etx.creates).decode()) + self.token_addr = TokenAddr(etx.creates.hex()) assert self.check_sigs(),'Signature check failed' def sign(self,tx_num_str,keys): # return True or False; don't exit or raise exception @@ -320,10 +319,10 @@ class EthereumMMGenTX(MMGenTX): return False def is_in_mempool(self): - return '0x'+self.coin_txid.decode() in [x['hash'] for x in g.rpch.parity_pendingTransactions()] + return '0x'+self.coin_txid in [x['hash'] for x in g.rpch.parity_pendingTransactions()] def is_in_wallet(self): - d = g.rpch.eth_getTransactionReceipt('0x'+self.coin_txid.decode()) + d = g.rpch.eth_getTransactionReceipt('0x'+self.coin_txid) if d and 'blockNumber' in d and d['blockNumber'] is not None: return 1 + int(g.rpch.eth_blockNumber(),16) - int(d['blockNumber'],16) return False @@ -365,7 +364,7 @@ class EthereumMMGenTX(MMGenTX): if prompt_user: self.confirm_send() - ret = None if bogus_send else g.rpch.eth_sendRawTransaction('0x'+self.hex.decode(),on_fail='return') + ret = None if bogus_send else g.rpch.eth_sendRawTransaction('0x'+self.hex,on_fail='return') from mmgen.rpc import rpc_error,rpc_errmsg if rpc_error(ret): @@ -376,7 +375,7 @@ class EthereumMMGenTX(MMGenTX): else: m = 'BOGUS transaction NOT sent: {}' if bogus_send else 'Transaction sent: {}' if not bogus_send: - assert ret == '0x'+self.coin_txid.decode(),'txid mismatch (after sending)' + assert ret == '0x'+self.coin_txid,'txid mismatch (after sending)' self.desc = 'sent transaction' msg(m.format(self.coin_txid.hl())) self.add_timestamp() @@ -418,8 +417,8 @@ class EthereumTokenMMGenTX(EthereumMMGenTX): def set_g_token(self): g.dcoin = self.dcoin - if is_hex_bytes(self.hex): return # for txsend we can leave g.token uninitialized - d = json.loads(self.hex.decode()) + if is_hex_str(self.hex): return # for txsend we can leave g.token uninitialized + d = json.loads(self.hex) if g.token.upper() == self.dcoin: g.token = d['token_addr'] elif g.token != d['token_addr']: diff --git a/mmgen/crypto.py b/mmgen/crypto.py index e768e4d3..11d21b43 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -20,7 +20,6 @@ crypto.py: Cryptographic and related routines for the MMGen suite """ -from binascii import hexlify from hashlib import sha256 from mmgen.common import * @@ -46,7 +45,7 @@ def scramble_seed(seed,scramble_key,hash_rounds): import hmac scr_seed = hmac.new(seed,scramble_key,sha256).digest() fs = 'Seed: {}\nScramble key: {}\nScrambled seed: {}' - dmsg(fs.format(hexlify(seed),scramble_key.decode('utf8'),hexlify(scr_seed))) + dmsg(fs.format(seed.hex(),scramble_key.decode(),scr_seed.hex())) return sha256_rounds(scr_seed,hash_rounds) def encrypt_seed(seed,key): @@ -77,7 +76,7 @@ def decrypt_seed(enc_seed,key,seed_id,key_id): # else: # qmsg('Generated IDs (Seed/Key): {}/{}'.format(chk2,chk1)) - dmsg('Decrypted seed: {}'.format(hexlify(dec_seed))) + dmsg('Decrypted seed: {}'.format(dec_seed.hex())) return dec_seed def encrypt_data(data,key,iv=1,desc='data',verify=True): @@ -120,7 +119,7 @@ def make_key(passwd,salt,hash_preset,desc='encryption key',from_what='passphrase msg_r('Generating {}{}...'.format(desc,from_what)) key = scrypt_hash_passphrase(passwd,salt,hash_preset) if opt.verbose or verbose: msg('done') - dmsg('Key: {}'.format(hexlify(key))) + dmsg('Key: {}'.format(key.hex())) return key def _get_random_data_from_user(uchars): @@ -189,7 +188,7 @@ def mmgen_encrypt(data,desc='data',hash_preset=''): qmsg("Using {} hash preset of '{}'".format(m,hp)) passwd = get_new_passphrase(desc,{}) key = make_key(passwd,salt,hp) - enc_d = encrypt_data(sha256(nonce+data).digest()+nonce+data,key,int(hexlify(iv),16),desc=desc) + enc_d = encrypt_data(sha256(nonce+data).digest()+nonce+data,key,int(iv.hex(),16),desc=desc) return salt+iv+enc_d def mmgen_decrypt(data,desc='data',hash_preset=''): @@ -204,7 +203,7 @@ def mmgen_decrypt(data,desc='data',hash_preset=''): qmsg("Using {} hash preset of '{}'".format(m,hp)) passwd = get_mmgen_passphrase(desc) key = make_key(passwd,salt,hp) - dec_d = decrypt_data(enc_d,key,int(hexlify(iv),16),desc) + dec_d = decrypt_data(enc_d,key,int(iv.hex(),16),desc) if dec_d[:_sha256_len] == sha256(dec_d[_sha256_len:]).digest(): vmsg('OK') return dec_d[_sha256_len+_nonce_len:] diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index b3c8b818..bfefddb3 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -269,13 +269,12 @@ def wipe_existing_key(): subprocess.call(['wipe','-cf',fn]) def create_key(): - from binascii import hexlify - kdata = hexlify(os.urandom(32)).decode() + kdata = os.urandom(32).hex() fn = os.path.join(tx_dir,key_fn) desc = 'key file {}'.format(fn) msg('Creating ' + desc) try: - with open(fn,'w') as f: f.write(kdata+'\n') + open(fn,'w').write(kdata+'\n') os.chmod(fn,0o400) msg('Wrote ' + desc) except: diff --git a/mmgen/obj.py b/mmgen/obj.py index b8bca224..1a92ef4b 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -24,7 +24,6 @@ import sys,os,unicodedata from decimal import * from mmgen.color import * from string import hexdigits,ascii_letters,digits -from binascii import hexlify def is_mmgen_seed_id(s): return SeedID(sid=s,on_fail='silent') def is_mmgen_idx(s): return AddrIdx(s,on_fail='silent') @@ -562,20 +561,19 @@ class TwLabel(str,InitErrors,MMGenObject): m = "{}\n{!r}: value cannot be converted to TwLabel" return cls.init_fail(m.format(e.args[0],s),on_fail) -class HexBytes(bytes,Hilite,InitErrors): +class HexStr(str,Hilite,InitErrors): color = 'red' trunc_ok = False - dtype = bytes + dtype = str def __new__(cls,s,on_fail='die',case='lower'): if type(s) == cls: return s assert case in ('upper','lower') cls.arg_chk(cls,on_fail) - if issubclass(type(s),bytes): s = s.decode() try: - assert type(s) == str,'not a string' + assert issubclass(type(s),str),'not a string or string subclass' assert set(s) <= set(getattr(hexdigits,case)()),'not {}case hexadecimal symbols'.format(case) assert not len(s) % 2,'odd-length string' - return cls.dtype.__new__(cls,s.encode() if cls.dtype == bytes else s) + return cls.dtype.__new__(cls,s) except Exception as e: m = "{!r}: value cannot be converted to {} (value is {})" return cls.init_fail(m.format(s,cls.__name__,e.args[0]),on_fail) @@ -583,11 +581,11 @@ class HexBytes(bytes,Hilite,InitErrors): class Str(str,Hilite): pass class Int(int,Hilite): pass -class HexBytesWithWidth(HexBytes): +class HexStrWithWidth(HexStr): color = 'nocolor' hexcase = 'lower' width = None - parent_cls = HexBytes + parent_cls = HexStr def __new__(cls,s,on_fail='die'): cls.arg_chk(cls,on_fail) try: @@ -598,23 +596,7 @@ class HexBytesWithWidth(HexBytes): m = "{}\n{!r}: value cannot be converted to {}" return cls.init_fail(m.format(e.args[0],s,cls.__name__),on_fail) -class CoinTxID(HexBytesWithWidth): color,width,hexcase = 'purple',64,'lower' - -class HexStr(str,Hilite,InitErrors): - color = 'red' - trunc_ok = False - dtype = str - def __new__(cls,s,on_fail='die',case='lower'): - return HexBytes.__new__(cls,s,on_fail=on_fail,case=case) - -class HexStrWithWidth(HexStr): - color = 'nocolor' - hexcase = 'lower' - width = None - parent_cls = HexStr - def __new__(cls,s,on_fail='die'): - return HexBytesWithWidth.__new__(cls,s,on_fail=on_fail) - +class CoinTxID(HexStrWithWidth): color,width,hexcase = 'purple',64,'lower' class WalletPassword(HexStrWithWidth): color,width,hexcase = 'blue',32,'lower' class MoneroViewKey(HexStrWithWidth): color,width,hexcase = 'cyan',64,'lower' class MMGenTxID(HexStrWithWidth): color,width,hexcase = 'red',6,'upper' @@ -634,18 +616,18 @@ class WifKey(str,Hilite,InitErrors): m = '{!r}: invalid value for WIF key ({})'.format(s,e.args[0]) return cls.init_fail(m,on_fail) -class PubKey(HexBytes,MMGenObject): # TODO: add some real checks +class PubKey(HexStr,MMGenObject): # TODO: add some real checks def __new__(cls,s,compressed,on_fail='die'): try: assert type(compressed) == bool,"'compressed' must be of type bool" - me = HexBytes.__new__(cls,s,case='lower',on_fail='raise') + me = HexStr.__new__(cls,s,case='lower',on_fail='raise') me.compressed = compressed return me except Exception as e: m = '{!r}: invalid value for pubkey ({})'.format(s,e.args[0]) return cls.init_fail(m,on_fail) -class PrivKey(bytes,Hilite,InitErrors,MMGenObject): +class PrivKey(str,Hilite,InitErrors,MMGenObject): color = 'red' width = 64 @@ -666,7 +648,7 @@ class PrivKey(bytes,Hilite,InitErrors,MMGenObject): assert s == None assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string' w2h = g.proto.wif2hex(wif) # raises exception on error - me = bytes.__new__(cls,w2h['hex']) + me = str.__new__(cls,w2h['hex']) me.compressed = w2h['compressed'] me.pubkey_type = w2h['pubkey_type'] me.wif = str.__new__(WifKey,wif) # check has been done @@ -680,13 +662,13 @@ class PrivKey(bytes,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 = bytes.__new__(cls,hexlify(s)) + me = str.__new__(cls,s.hex()) else: - me = bytes.__new__(cls,g.proto.preprocess_key(hexlify(s),pubkey_type)) + me = str.__new__(cls,g.proto.preprocess_key(s.hex(),pubkey_type)) me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise') me.compressed = compressed me.pubkey_type = pubkey_type - me.orig_hex = hexlify(s) # save the non-preprocessed key + me.orig_hex = s.hex() # save the non-preprocessed key return me except Exception as e: fs = "Key={!r}\nCompressed={}\nValue pair cannot be converted to PrivKey\n({})" diff --git a/mmgen/protocol.py b/mmgen/protocol.py index 5b12e4e5..eba0af2c 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -21,17 +21,16 @@ protocol.py: Coin protocol functions, classes and methods """ import sys,os,hashlib -from binascii import hexlify,unhexlify from mmgen.util import msg,pmsg,ymsg,Msg,pdie,ydie from mmgen.obj import MMGenObject,BTCAmt,LTCAmt,BCHAmt,B2XAmt,ETHAmt from mmgen.globalvars import g import mmgen.bech32 as bech32 def hash160(hexnum): # take hex, return hex - OP_HASH160 - return hashlib.new('ripemd160',hashlib.sha256(unhexlify(hexnum)).digest()).hexdigest().encode() + return hashlib.new('ripemd160',hashlib.sha256(bytes.fromhex(hexnum)).digest()).hexdigest() def hash256(hexnum): # take hex, return hex - OP_HASH256 - return hashlib.sha256(hashlib.sha256(unhexlify(hexnum)).digest()).hexdigest().encode() + return hashlib.sha256(hashlib.sha256(bytes.fromhex(hexnum)).digest()).hexdigest() _b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @@ -43,7 +42,7 @@ _b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20) def _b58chk_encode(hexstr): - lzeroes = (len(hexstr) - len(hexstr.lstrip(b'0'))) // 2 + lzeroes = (len(hexstr) - len(hexstr.lstrip('0'))) // 2 def b58enc(n): while n: yield _b58a[n % 58] @@ -53,10 +52,9 @@ def _b58chk_encode(hexstr): def _b58chk_decode(s): lzeroes = len(s) - len(s.lstrip('1')) hexstr = '{}{:x}'.format( - '00' * lzeroes, - sum(_b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1])) - ).encode() - if len(hexstr) % 2: hexstr = b'0' + hexstr + '00' * lzeroes, + sum(_b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1])) ) + if len(hexstr) % 2: hexstr = '0' + hexstr if hexstr[-8:] != hash256(hexstr[:-8])[:8]: raise ValueError('_b58chk_decode(): {}: incorrect checksum for {}, expected {}'.format( hexstr[-8:],hexstr[:-8],hash256(hexstr[:-8])[:8])) @@ -67,8 +65,8 @@ class BitcoinProtocol(MMGenObject): name = 'bitcoin' daemon_name = 'bitcoind' daemon_family = 'bitcoind' - addr_ver_num = { 'p2pkh': (b'00','1'), 'p2sh': (b'05','3') } - wif_ver_num = { 'std': b'80' } + addr_ver_num = { 'p2pkh': ('00','1'), 'p2sh': ('05','3') } + wif_ver_num = { 'std': '80' } mmtypes = ('L','C','S','B') dfl_mmtype = 'L' data_subdir = '' @@ -90,7 +88,7 @@ class BitcoinProtocol(MMGenObject): base_coin = 'BTC' # From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00, # but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal). - witness_vernum_hex = b'00' + witness_vernum_hex = '00' witness_vernum = int(witness_vernum_hex,16) bech32_hrp = 'bc' sign_mode = 'daemon' @@ -126,7 +124,7 @@ class BitcoinProtocol(MMGenObject): @classmethod def hex2wif(cls,hexpriv,pubkey_type,compressed): # PrivKey assert len(hexpriv) == cls.privkey_len*2, '{} bytes: incorrect private key length!'.format(len(hexpriv)//2) - return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + (b'',b'01')[bool(compressed)]) + return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + ('','01')[bool(compressed)]) @classmethod def wif2hex(cls,wif): @@ -138,7 +136,7 @@ class BitcoinProtocol(MMGenObject): key = key[len(v):] assert pubkey_type,'invalid WIF version number' if len(key) == 66: - assert key[-2:] == b'01','invalid compressed key suffix' + assert key[-2:] == '01','invalid compressed key suffix' compressed = True else: assert len(key) == 64,'invalid key length' @@ -156,7 +154,7 @@ class BitcoinProtocol(MMGenObject): msg('{}: Invalid witness version number'.format(ret[0])) elif ret[1]: return { - 'hex': hexlify(bytes(ret[1])), + 'hex': bytes(ret[1]).hex(), 'format': 'bech32' } if return_dict else True return False @@ -191,7 +189,7 @@ class BitcoinProtocol(MMGenObject): # https://bitcoincore.org/en/segwit_wallet_dev/ # The P2SH redeemScript is always 22 bytes. It starts with a OP_0, followed # by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash}) - return cls.witness_vernum_hex + b'14' + hash160(pubhex) + return cls.witness_vernum_hex + '14' + hash160(pubhex) @classmethod def pubhex2segwitaddr(cls,pubhex): @@ -199,12 +197,12 @@ class BitcoinProtocol(MMGenObject): @classmethod def pubhash2bech32addr(cls,pubhash): - d = list(unhexlify(pubhash)) + d = list(bytes.fromhex(pubhash)) return bech32.bech32_encode(cls.bech32_hrp,[cls.witness_vernum]+bech32.convertbits(d,8,5)) class BitcoinTestnetProtocol(BitcoinProtocol): - addr_ver_num = { 'p2pkh': (b'6f',('m','n')), 'p2sh': (b'c4','2') } - wif_ver_num = { 'std': b'ef' } + addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') } + wif_ver_num = { 'std': 'ef' } data_subdir = 'testnet' daemon_data_subdir = 'testnet3' rpc_port = 18332 @@ -233,8 +231,8 @@ class BitcoinCashProtocol(BitcoinProtocol): class BitcoinCashTestnetProtocol(BitcoinCashProtocol): rpc_port = 18442 - addr_ver_num = { 'p2pkh': (b'6f',('m','n')), 'p2sh': (b'c4','2') } - wif_ver_num = { 'std': b'ef' } + addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') } + wif_ver_num = { 'std': 'ef' } data_subdir = 'testnet' daemon_data_subdir = 'testnet3' @@ -250,8 +248,8 @@ class B2XProtocol(BitcoinProtocol): ] class B2XTestnetProtocol(B2XProtocol): - addr_ver_num = { 'p2pkh': (b'6f',('m','n')), 'p2sh': (b'c4','2') } - wif_ver_num = { 'std': b'ef' } + addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') } + wif_ver_num = { 'std': 'ef' } data_subdir = 'testnet' daemon_data_subdir = 'testnet5' rpc_port = 18338 @@ -262,8 +260,8 @@ class LitecoinProtocol(BitcoinProtocol): daemon_name = 'litecoind' daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Litecoin') if g.platform == 'win' \ else os.path.join(g.home_dir,'.litecoin') - addr_ver_num = { 'p2pkh': (b'30','L'), 'p2sh': (b'32','M'), 'p2sh2': (b'05','3') } # 'p2sh' is new fmt - wif_ver_num = { 'std': b'b0' } + addr_ver_num = { 'p2pkh': ('30','L'), 'p2sh': ('32','M'), 'p2sh2': ('05','3') } # 'p2sh' is new fmt + wif_ver_num = { 'std': 'b0' } mmtypes = ('L','C','S','B') secs_per_block = 150 rpc_port = 9332 @@ -275,8 +273,8 @@ class LitecoinProtocol(BitcoinProtocol): class LitecoinTestnetProtocol(LitecoinProtocol): # addr ver nums same as Bitcoin testnet, except for 'p2sh' - addr_ver_num = { 'p2pkh': (b'6f',('m','n')), 'p2sh': (b'3a','Q'), 'p2sh2': (b'c4','2') } - wif_ver_num = { 'std': b'ef' } # same as Bitcoin testnet + addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('3a','Q'), 'p2sh2': ('c4','2') } + wif_ver_num = { 'std': 'ef' } # same as Bitcoin testnet data_subdir = 'testnet' daemon_data_subdir = 'testnet4' rpc_port = 19332 @@ -293,11 +291,11 @@ class DummyWIF(object): n = cls.name.capitalize() assert pubkey_type == cls.pubkey_type,'{}: invalid pubkey_type for {}!'.format(pubkey_type,n) assert compressed == False,'{} does not support compressed pubkeys!'.format(n) - return hexpriv.decode() + return hexpriv @classmethod def wif2hex(cls,wif): - return { 'hex':wif.encode(), 'pubkey_type':cls.pubkey_type, 'compressed':False } + return { 'hex':wif, 'pubkey_type':cls.pubkey_type, 'compressed':False } class EthereumProtocol(DummyWIF,BitcoinProtocol): @@ -323,7 +321,7 @@ class EthereumProtocol(DummyWIF,BitcoinProtocol): def verify_addr(cls,addr,hex_width,return_dict=False): from mmgen.util import is_hex_str_lc if is_hex_str_lc(addr) and len(addr) == cls.addr_width: - return { 'hex': addr.encode(), 'format': 'ethereum' } if return_dict else True + return { 'hex': addr, 'format': 'ethereum' } if return_dict else True if g.debug: Msg("Invalid address '{}'".format(addr)) return False @@ -331,7 +329,7 @@ class EthereumProtocol(DummyWIF,BitcoinProtocol): def pubhash2addr(cls,pubkey_hash,p2sh): assert len(pubkey_hash) == 40,'{}: invalid length for pubkey hash'.format(len(pubkey_hash)) assert not p2sh,'Ethereum has no P2SH address format' - return pubkey_hash.decode() + return pubkey_hash class EthereumTestnetProtocol(EthereumProtocol): data_subdir = 'testnet' @@ -352,18 +350,18 @@ class ZcashProtocol(BitcoinProtocolAddrgen): name = 'zcash' base_coin = 'ZEC' addr_ver_num = { - 'p2pkh': (b'1cb8','t1'), - 'p2sh': (b'1cbd','t3'), - 'zcash_z': (b'169a','zc'), - 'viewkey': (b'a8abd3','ZiVK') } - wif_ver_num = { 'std': b'80', 'zcash_z': b'ab36' } + 'p2pkh': ('1cb8','t1'), + 'p2sh': ('1cbd','t3'), + 'zcash_z': ('169a','zc'), + 'viewkey': ('a8abd3','ZiVK') } + wif_ver_num = { 'std': '80', 'zcash_z': 'ab36' } mmtypes = ('L','C','Z') dfl_mmtype = 'L' @classmethod def preprocess_key(cls,hexpriv,pubkey_type): # zero the first four bits if pubkey_type == 'zcash_z': - return '{:02x}'.format(int(hexpriv[:2],16) & 0x0f).encode() + hexpriv[2:] + return '{:02x}'.format(int(hexpriv[:2],16) & 0x0f) + hexpriv[2:] else: return hexpriv @@ -378,18 +376,18 @@ class ZcashProtocol(BitcoinProtocolAddrgen): raise ValueError('{}: incorrect pubkey_hash length'.format(hl)) class ZcashTestnetProtocol(ZcashProtocol): - wif_ver_num = { 'std': b'ef', 'zcash_z': b'ac08' } + wif_ver_num = { 'std': 'ef', 'zcash_z': 'ac08' } addr_ver_num = { - 'p2pkh': (b'1d25','tm'), - 'p2sh': (b'1cba','t2'), - 'zcash_z': (b'16b6','zt'), - 'viewkey': (b'a8ac0c','ZiVt') } + 'p2pkh': ('1d25','tm'), + 'p2sh': ('1cba','t2'), + 'zcash_z': ('16b6','zt'), + 'viewkey': ('a8ac0c','ZiVt') } # https://github.com/monero-project/monero/blob/master/src/cryptonote_config.h class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen): name = 'monero' base_coin = 'XMR' - addr_ver_num = { 'monero': (b'12','4'), 'monero_sub': (b'2a','8') } # 18,42 + addr_ver_num = { 'monero': ('12','4'), 'monero_sub': ('2a','8') } # 18,42 wif_ver_num = {} mmtypes = ('M',) dfl_mmtype = 'M' @@ -399,8 +397,8 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen): @classmethod def preprocess_key(cls,hexpriv,pubkey_type): # reduce key from mmgen.ed25519 import l - n = int(hexlify(unhexlify(hexpriv)[::-1]),16) % l - return hexlify(unhexlify('{:064x}'.format(n))[::-1]) + n = int(bytes.fromhex(hexpriv)[::-1].hex(),16) % l + return bytes.fromhex('{:064x}'.format(n))[::-1].hex() @classmethod def verify_addr(cls,addr,hex_width,return_dict=False): @@ -408,7 +406,7 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen): def b58dec(addr_str): from mmgen.util import baseconv l = len(addr_str) - a = b''.join([baseconv.tohex(addr_str[i*11:i*11+11],'b58',pad=16) for i in range(l//11)]) + a = ''.join([baseconv.tohex(addr_str[i*11:i*11+11],'b58',pad=16) for i in range(l//11)]) b = baseconv.tohex(addr_str[-(l%11):],'b58',pad=10) return a + b @@ -418,13 +416,13 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen): ret = b58dec(addr) import sha3 - chk = sha3.keccak_256(unhexlify(ret)[:-4]).hexdigest()[:8] - assert chk.encode() == ret[-8:],'Incorrect checksum' + chk = sha3.keccak_256(bytes.fromhex(ret)[:-4]).hexdigest()[:8] + assert chk == ret[-8:],'{}: incorrect checksum. Correct value: {}'.format(ret[-8:],chk) return { 'hex': ret, 'format': 'monero' } if return_dict else True class MoneroTestnetProtocol(MoneroProtocol): - addr_ver_num = { 'monero': (b'35','4'), 'monero_sub': (b'3f','8') } # 53,63 + addr_ver_num = { 'monero': ('35','4'), 'monero_sub': ('3f','8') } # 53,63 class CoinProtocol(MMGenObject): coins = { @@ -484,17 +482,17 @@ def make_init_genonly_altcoins_str(data): if proto in globals(): return '' if coin.lower() in CoinProtocol.coins: return '' - def num2hexbytes(n): - return "b'{:0{}x}'".format(n,(4,2)[n < 256]) + def num2hexstr(n): + return "'{:0{}x}'".format(n,(4,2)[n < 256]) o = ['class {}(Bitcoin{}ProtocolAddrgen):'.format(proto,tn_str)] o += ["base_coin = '{}'".format(coin)] o += ["name = '{}'".format(e[0].lower())] o += ["nameCaps = '{}'".format(e[0])] - a = "addr_ver_num = {{ 'p2pkh': ({},{!r})".format(num2hexbytes(e[3][0]),e[3][1]) - b = ", 'p2sh': ({},{!r})".format(num2hexbytes(e[4][0]),e[4][1]) if e[4] else '' + a = "addr_ver_num = {{ 'p2pkh': ({},{!r})".format(num2hexstr(e[3][0]),e[3][1]) + b = ", 'p2sh': ({},{!r})".format(num2hexstr(e[4][0]),e[4][1]) if e[4] else '' o += [a+b+' }'] - o += ["wif_ver_num = {{ 'std': {} }}".format(num2hexbytes(e[2]))] + o += ["wif_ver_num = {{ 'std': {} }}".format(num2hexstr(e[2]))] o += ["mmtypes = ('L','C'{})".format(",'S'" if e[5] else '')] o += ["dfl_mmtype = '{}'".format('L')] return '\n\t'.join(o) + '\n' diff --git a/mmgen/rpc.py b/mmgen/rpc.py index c298f836..f30e9813 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -110,13 +110,13 @@ class CoinDaemonRPCConnection(object): dmsg_rpc(' RPC POST data ==> {}\n'.format(p)) ca_type = self.coin_amt_type if hasattr(self,'coin_amt_type') else str - from mmgen.obj import CoinTxID,HexBytes + from mmgen.obj import HexStr class MyJSONEncoder(json.JSONEncoder): def default(self,obj): if isinstance(obj,g.proto.coin_amt): return ca_type(obj) - elif isinstance(obj,CoinTxID) or isinstance(obj,HexBytes): - return obj.decode() + elif isinstance(obj,HexStr): + return obj else: return json.JSONEncoder.default(self,obj) diff --git a/mmgen/seed.py b/mmgen/seed.py index 15b6c9ca..da413905 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -21,7 +21,6 @@ seed.py: Seed-related classes and methods for the MMGen suite """ import os -from binascii import hexlify,unhexlify from mmgen.common import * from mmgen.obj import * @@ -55,7 +54,7 @@ class Seed(MMGenObject): die(3,'{}: invalid seed length'.format(len(seed_bin))) self.data = seed_bin - self.hexdata = hexlify(seed_bin) + self.hexdata = seed_bin.hex() self.sid = SeedID(seed=self) self.length = len(seed_bin) * 8 @@ -485,7 +484,7 @@ class Mnemonic (SeedSourceUnenc): # Internal error, so just die compare_or_die(' '.join(ret),'recomputed mnemonic',' '.join(mn),'original',e='Internal error') - self.seed = Seed(unhexlify(hexseed)) + self.seed = Seed(bytes.fromhex(hexseed)) self.ssdata.mnemonic = mn check_usr_seed_len(self.seed.length) @@ -542,7 +541,7 @@ class SeedFile (SeedSourceUnenc): return True -class HexSeedFile (SeedSourceUnenc): +class HexSeedFile(SeedSourceUnenc): stdin_ok = True fmt_codes = 'seedhex','hexseed','hex','mmhex' @@ -553,7 +552,7 @@ class HexSeedFile (SeedSourceUnenc): h = self.seed.hexdata self.ssdata.chksum = make_chksum_6(h) self.ssdata.hexseed = h - self.fmt_data = '{} {}\n'.format(self.ssdata.chksum, split_into_cols(4,h.decode())) + self.fmt_data = '{} {}\n'.format(self.ssdata.chksum, split_into_cols(4,h)) def _deformat(self): desc = self.desc @@ -582,7 +581,7 @@ class HexSeedFile (SeedSourceUnenc): if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True): return False - self.seed = Seed(unhexlify(hstr)) + self.seed = Seed(bytes.fromhex(hstr)) self.ssdata.chksum = chk self.ssdata.hexseed = hstr @@ -869,7 +868,7 @@ to exit and re-run the program with the '--old-incog-fmt' option. self.fmt_data = d.iv + encrypt_data( d.salt + d.enc_seed, d.wrapper_key, - int(hexlify(d.iv),16), + int(d.iv.hex(),16), self.desc) # print len(self.fmt_data) @@ -923,7 +922,7 @@ to exit and re-run the program with the '--old-incog-fmt' option. # IV is used BOTH to initialize counter and to salt password! key = make_key(d.passwd, d.iv, d.hash_preset, 'wrapper key') dd = decrypt_data(d.enc_incog_data, key, - int(hexlify(d.iv),16), 'incog data') + int(d.iv.hex(),16), 'incog data') d.salt = dd[0:g.salt_len] d.enc_seed = dd[g.salt_len:] diff --git a/mmgen/sha2.py b/mmgen/sha2.py index 4c6fa636..f2c912cf 100755 --- a/mmgen/sha2.py +++ b/mmgen/sha2.py @@ -22,7 +22,6 @@ sha2.py: A non-optimized but very compact implementation of the SHA2 hash SHA256Compress (unpadded SHA256, required for Zcash addresses) """ -from binascii import hexlify from struct import pack,unpack class Sha2(object): @@ -103,7 +102,7 @@ class Sha2(object): return b''.join((pack(self.word_fmt,w) for w in self.H)) def hexdigest(self): - return hexlify(self.digest()) + return self.digest().hex() def compute(self): for i in range(0,len(self.M),16): diff --git a/mmgen/tool.py b/mmgen/tool.py index 1438e5bb..e6d8eccf 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -20,8 +20,6 @@ tool.py: Routines for the 'mmgen-tool' utility """ -from binascii import hexlify,unhexlify - from mmgen.protocol import hash160 from mmgen.common import * from mmgen.crypto import * @@ -248,20 +246,20 @@ class MMGenToolCmdUtil(MMGenToolCmdBase): def randhex(self,nbytes='32'): "print 'n' bytes (default 32) of random data in hex format" - return hexlify(get_random(int(nbytes))) + return get_random(int(nbytes)).hex() def hexreverse(self,hexstr:'sstr'): "reverse bytes of a hexadecimal string" - return hexlify(unhexlify(hexstr.strip())[::-1]) + return bytes.fromhex(hexstr.strip())[::-1].hex() def hexlify(self,infile:str): "convert bytes in file to hexadecimal (use '-' for stdin)" data = get_data_from_file(infile,dash=True,silent=True,binary=True) - return hexlify(data) + return data.hex() def unhexlify(self,hexstr:'sstr'): "convert hexadecimal value to bytes (warning: outputs binary data)" - return unhexlify(hexstr.encode()) + return bytes.fromhex(hexstr) def hexdump(self,infile:str,cols=8,line_nums=True): "create hexdump of data from file (use '-' for stdin)" @@ -286,7 +284,7 @@ class MMGenToolCmdUtil(MMGenToolCmdBase): if file_input: b = get_data_from_file(string_or_bytes,binary=True) elif hex_input: b = decode_pretty_hexdump(string_or_bytes) else: b = string_or_bytes - return sha256(sha256(b.encode()).digest()).hexdigest().encode() + return sha256(sha256(b.encode()).digest()).hexdigest() def id6(self,infile:str): "generate 6-character MMGen ID for a file (use '-' for stdin)" @@ -309,15 +307,15 @@ class MMGenToolCmdUtil(MMGenToolCmdBase): def bytestob58(self,infile:str,pad=0): "convert bytes to base 58 (supply data via STDIN)" data = get_data_from_file(infile,dash=True,silent=True,binary=True) - return baseconv.fromhex(hexlify(data),'b58',pad=pad,tostr=True) + return baseconv.fromhex(data.hex(),'b58',pad=pad,tostr=True) def b58tobytes(self,b58num:'sstr',pad=0): "convert a base 58 number to bytes (warning: outputs binary data)" - return unhexlify(baseconv.tohex(b58num,'b58',pad=pad)) + return bytes.fromhex(baseconv.tohex(b58num,'b58',pad=pad)) def hextob58(self,hexstr:'sstr',pad=0): "convert a hexadecimal number to base 58" - return baseconv.fromhex(hexstr.encode(),'b58',pad=pad,tostr=True) + return baseconv.fromhex(hexstr,'b58',pad=pad,tostr=True) def b58tohex(self,b58num:'sstr',pad=0): "convert a base 58 number to hexadecimal" @@ -326,7 +324,7 @@ class MMGenToolCmdUtil(MMGenToolCmdBase): def hextob58chk(self,hexstr:'sstr'): "convert a hexadecimal number to base58-check encoding" from mmgen.protocol import _b58chk_encode - return _b58chk_encode(hexstr.encode()) + return _b58chk_encode(hexstr) def b58chktohex(self,b58chk_num:'sstr'): "convert a base58-check encoded number to hexadecimal" @@ -335,7 +333,7 @@ class MMGenToolCmdUtil(MMGenToolCmdBase): def hextob32(self,hexstr:'sstr',pad=0): "convert a hexadecimal number to MMGen's flavor of base 32" - return baseconv.fromhex(hexstr.encode(),'b32',pad,tostr=True) + return baseconv.fromhex(hexstr,'b32',pad,tostr=True) def b32tohex(self,b32num:'sstr',pad=0): "convert an MMGen-flavor base 32 number to hexadecimal" @@ -370,7 +368,7 @@ class MMGenToolCmdCoin(MMGenToolCmdBase): def hex2wif(self,privhex:'sstr'): "convert a private key from hex to WIF format" init_generators('at') - return g.proto.hex2wif(privhex.encode(),pubkey_type=at.pubkey_type,compressed=at.compressed) + return g.proto.hex2wif(privhex,pubkey_type=at.pubkey_type,compressed=at.compressed) def wif2addr(self,wifkey:'sstr'): "generate a coin address from a key in WIF format" @@ -398,7 +396,7 @@ class MMGenToolCmdCoin(MMGenToolCmdBase): def privhex2addr(self,privhex:'sstr',output_pubhex=False): "generate coin address from private key in hex format" init_generators() - pk = PrivKey(unhexlify(privhex),compressed=at.compressed,pubkey_type=at.pubkey_type) + pk = PrivKey(bytes.fromhex(privhex),compressed=at.compressed,pubkey_type=at.pubkey_type) ph = kg.to_pubhex(pk) return ph if output_pubhex else ag.to_addr(ph) @@ -409,9 +407,9 @@ class MMGenToolCmdCoin(MMGenToolCmdBase): def pubhex2addr(self,pubkeyhex:'sstr'): "convert a hex pubkey to an address" if opt.type == 'segwit': - return g.proto.pubhex2segwitaddr(pubkeyhex.encode()) + return g.proto.pubhex2segwitaddr(pubkeyhex) else: - return self.pubhash2addr(hash160(pubkeyhex.encode()).decode()) + return self.pubhash2addr(hash160(pubkeyhex)) def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new "convert a hex pubkey to a Segwit P2SH-P2WPKH redeem script" @@ -423,15 +421,15 @@ class MMGenToolCmdCoin(MMGenToolCmdBase): assert opt.type == 'segwit','This command is meaningful only for --type=segwit' assert redeem_scripthex[:4] == '0014','{!r}: invalid redeem script'.format(redeem_scripthex) assert len(redeem_scripthex) == 44,'{} bytes: invalid redeem script length'.format(len(redeem_scripthex)//2) - return self.pubhash2addr(self.hash160(redeem_scripthex).decode()) + return self.pubhash2addr(self.hash160(redeem_scripthex)) def pubhash2addr(self,pubhashhex:'sstr'): "convert public key hash to address" if opt.type == 'bech32': - return g.proto.pubhash2bech32addr(pubhashhex.encode()) + return g.proto.pubhash2bech32addr(pubhashhex) else: init_generators('at') - return g.proto.pubhash2addr(pubhashhex.encode(),at.addr_fmt=='p2sh') + return g.proto.pubhash2addr(pubhashhex,at.addr_fmt=='p2sh') def addr2pubhash(self,addr:'sstr'): "convert coin address to public key hash" @@ -446,7 +444,7 @@ class MMGenToolCmdMnemonic(MMGenToolCmdBase): """ def _do_random_mn(self,nbytes:int,wordlist_id:str): assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32' - hexrand = hexlify(get_random(nbytes)) + hexrand = get_random(nbytes).hex() Vmsg('Seed: {}'.format(hexrand)) return self.hex2mn(hexrand,wordlist_id=wordlist_id) @@ -466,7 +464,7 @@ class MMGenToolCmdMnemonic(MMGenToolCmdBase): "convert a 16, 24 or 32-byte hexadecimal number to a mnemonic" opt.out_fmt = 'words' from mmgen.seed import SeedSource - s = SeedSource(seed=unhexlify(hexstr)) + s = SeedSource(seed=bytes.fromhex(hexstr)) s._format() return ' '.join(s.ssdata.mnemonic) @@ -820,7 +818,7 @@ class MMGenToolCmdMonero(MMGenToolCmdBase): p = pexpect.spawn('monero-wallet-cli --generate-from-spend-key {}'.format(fn)) if g.debug: p.logfile = sys.stdout my_expect(p,'Awaiting initial prompt','Secret spend key: ') - my_sendline(p,'',d.sec.decode(),65) + my_sendline(p,'',d.sec,65) my_expect(p,'','Enter.* new.* password.*: ',regex=True) my_sendline(p,'Sending password',d.wallet_passwd,33) my_expect(p,'','Confirm password: ') diff --git a/mmgen/tw.py b/mmgen/tw.py index 8cade681..3f38652e 100755 --- a/mmgen/tw.py +++ b/mmgen/tw.py @@ -63,7 +63,7 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel: twmmid = MMGenImmutableAttr('twmmid','TwMMGenID') addr = MMGenImmutableAttr('addr','CoinAddr') confs = MMGenImmutableAttr('confs',int,typeconv=False) - scriptPubKey = MMGenImmutableAttr('scriptPubKey','HexBytes') + scriptPubKey = MMGenImmutableAttr('scriptPubKey','HexStr') days = MMGenListItemAttr('days',int,typeconv=False) skip = MMGenListItemAttr('skip',str,typeconv=False,reassign_ok=True) @@ -231,7 +231,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program. out.append(fs.format( n=str(n+1)+')', t='' if not i.txid else \ ' ' * (tx_w-4) + '|...' if i.skip == 'txid' \ - else i.txid.decode()[:tx_w-len(txdots)]+txdots, + else i.txid[:tx_w-len(txdots)] + txdots, v=i.vout, a=addr_out, A=i.amt.fmt(color=True,prec=self.disp_prec), diff --git a/mmgen/tx.py b/mmgen/tx.py index 10f6253e..00fad275 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -22,7 +22,6 @@ tx.py: Transaction routines for the MMGen suite import sys,os,json from stat import * -from binascii import unhexlify from mmgen.common import * from mmgen.obj import * @@ -98,8 +97,8 @@ def segwit_is_active(exit_on_error=False): return False def bytes2int(hex_bytes): - r = hexlify(unhexlify(hex_bytes)[::-1]) - if hexlify(bytes([r[0]])) in b'89abcdef': + r = bytes.fromhex(hex_bytes)[::-1].hex() + if r[0] in '89abcdef': # sign bit is set die(3,"{}: Negative values not permitted in transaction!".format(hex_bytes)) return int(r,16) @@ -107,11 +106,11 @@ def bytes2coin_amt(hex_bytes): return g.proto.coin_amt(bytes2int(hex_bytes) * g.proto.coin_amt.min_coin_unit) def scriptPubKey2addr(s): - if len(s) == 50 and s[:6] == b'76a914' and s[-4:] == b'88ac': + if len(s) == 50 and s[:6] == '76a914' and s[-4:] == '88ac': return g.proto.pubhash2addr(s[6:-4],p2sh=False),'p2pkh' - elif len(s) == 46 and s[:4] == b'a914' and s[-2:] == b'87': + elif len(s) == 46 and s[:4] == 'a914' and s[-2:] == '87': return g.proto.pubhash2addr(s[4:-2],p2sh=True),'p2sh' - elif len(s) == 44 and s[:4] == g.proto.witness_vernum_hex + b'14': + elif len(s) == 44 and s[:4] == g.proto.witness_vernum_hex + '14': return g.proto.pubhash2bech32addr(s[4:]),'bech32' else: raise NotImplementedError('Unknown scriptPubKey ({})'.format(s)) @@ -119,7 +118,7 @@ def scriptPubKey2addr(s): from collections import OrderedDict class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types def __init__(self,txhex): - tx = list(unhexlify(txhex)) + tx = list(bytes.fromhex(txhex)) tx_copy = tx[:] d = { 'raw_tx': [] } @@ -127,7 +126,7 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types ret = l[:n] if not skip: d['raw_tx'] += ret del l[:n] - return hexlify(bytes(ret[::-1] if reverse else ret)) + return bytes(ret[::-1] if reverse else ret).hex() # https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers # For example, the number 515 is encoded as 0xfd0302. @@ -135,8 +134,8 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types 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(bytes(l[:bytes_len][::-1])),16) - if sub_null: d['raw_tx'] += b'\0' + ret = int(bytes(l[:bytes_len][::-1]).hex(),16) + if sub_null: d['raw_tx'] += b'\x00' elif not skip: d['raw_tx'] += l[:bytes_len] del l[:bytes_len] return ret @@ -145,7 +144,7 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types has_witness = tx[0] == 0 if has_witness: u = hshift(tx,2,skip=True) - if u != b'0001': + if u != '0001': raise IllegalWitnessFlagValue("'{}': Illegal value for flag in transaction!".format(u)) del tx_copy[-len(tx)-2:-len(tx)] @@ -186,8 +185,8 @@ 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(bytes(tx_copy)).digest()).digest()[::-1]) - d['unsigned_hex'] = hexlify(bytes(d['raw_tx'])) + d['txid'] = sha256(sha256(bytes(tx_copy)).digest()).digest()[::-1].hex() + d['unsigned_hex'] = bytes(d['raw_tx']).hex() del d['raw_tx'] keys = 'txid','version','lock_time','witness_size','num_txins','txins','num_txouts','txouts','unsigned_hex' @@ -240,7 +239,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam class MMGenTxInput(MMGenListItem): for k in txio_attrs: locals()[k] = txio_attrs[k] # in lieu of inheritance - scriptPubKey = MMGenListItemAttr('scriptPubKey','HexBytes') + scriptPubKey = MMGenListItemAttr('scriptPubKey','HexStr') sequence = MMGenListItemAttr('sequence',int,typeconv=False) class MMGenTxOutput(MMGenListItem): @@ -279,7 +278,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam self.outputs = self.MMGenTxOutputList() self.send_amt = g.proto.coin_amt('0') # total amt minus change self.fee = g.proto.coin_amt('0') - self.hex = b'' # raw serialized hex transaction + self.hex = '' # raw serialized hex transaction self.label = MMGenTXLabel('') self.txid = '' self.coin_txid = '' @@ -362,14 +361,14 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam die(2,'{}: duplicate address in transaction {}'.format(attr,io_str)) def update_txid(self): - self.txid = MMGenTxID(make_chksum_6(unhexlify(self.hex)).upper()) + self.txid = MMGenTxID(make_chksum_6(bytes.fromhex(self.hex)).upper()) def create_raw(self): i = [{'txid':e.txid,'vout':e.vout} for e in self.inputs] if self.inputs[0].sequence: i[0]['sequence'] = self.inputs[0].sequence o = {e.addr:e.amt for e in self.outputs} - self.hex = HexBytes(g.rpch.createrawtransaction(i,o)) + self.hex = HexStr(g.rpch.createrawtransaction(i,o)) self.update_txid() # returns true if comment added or changed @@ -625,11 +624,11 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam self.timestamp = make_timestamp() def get_hex_locktime(self): - return int(hexlify(unhexlify(self.hex[-8:])[::-1]),16) + return int(bytes.fromhex(self.hex[-8:])[::-1].hex(),16) def set_hex_locktime(self,val): assert type(val) == int,'locktime value not an integer' - self.hex = self.hex[:-8] + hexlify(unhexlify('{:08x}'.format(val))[::-1]) + self.hex = self.hex[:-8] + bytes.fromhex('{:08x}'.format(val))[::-1].hex() def get_blockcount(self): return int(g.rpch.getblockcount()) @@ -653,7 +652,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam self.blockcount, ('',' LT={}'.format(self.locktime))[bool(self.locktime)] ), - self.hex.decode(), + self.hex, repr([amt_to_str(e.__dict__) for e in self.inputs]), repr([amt_to_str(e.__dict__) for e in self.outputs]) ] @@ -661,7 +660,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam lines.append(baseconv.b58encode(self.label.encode())) if self.coin_txid: if not self.label: lines.append('-') # keep old tx files backwards compatible - lines.append(self.coin_txid.decode()) + lines.append(self.coin_txid) self.chksum = make_chksum_6(' '.join(lines)) self.fmt_data = '\n'.join([self.chksum] + lines)+'\n' @@ -721,13 +720,13 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam return False try: - self.hex = HexBytes(ret['hex']) + self.hex = HexStr(ret['hex']) self.compare_size_and_estimated_size() dt = DeserializedTX(self.hex) self.check_hex_tx_matches_mmgen_tx(dt) self.coin_txid = CoinTxID(dt['txid'],on_fail='raise') self.check_sigs(dt) - if not self.coin_txid.decode() == g.rpch.decoderawtransaction(ret['hex'])['txid']: + if not self.coin_txid == g.rpch.decoderawtransaction(ret['hex'])['txid']: raise BadMMGenTxID('txid mismatch (after signing)') msg('OK') return True @@ -782,7 +781,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam check_equal('outputs',d_hex,d_mmgen) uh = deserial_tx['unsigned_hex'] - if str(self.txid) != make_chksum_6(unhexlify(uh)).upper(): + if str(self.txid) != make_chksum_6(bytes.fromhex(uh)).upper(): raise TxHexMismatch('MMGen TxID ({}) does not match hex transaction data!\n{}'.format(self.txid,m)) def check_pubkey_scripts(self): @@ -807,13 +806,13 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam fs = "Hex TX has {} scriptSig but input is of type '{}'!" for n in range(len(txins)): ti,mmti = txins[n],self.inputs[n] - if ti['scriptSig'] == b'' or ( len(ti['scriptSig']) == 46 and # native P2WPKH or P2SH-P2WPKH - ti['scriptSig'][:6] == b'16' + g.proto.witness_vernum_hex + b'14' ): + if ti['scriptSig'] == '' or ( len(ti['scriptSig']) == 46 and # native P2WPKH or P2SH-P2WPKH + ti['scriptSig'][:6] == '16' + g.proto.witness_vernum_hex + '14' ): assert 'witness' in ti, 'missing witness' assert type(ti['witness']) == list and len(ti['witness']) == 2, 'malformed witness' assert len(ti['witness'][1]) == 66, 'incorrect witness pubkey length' assert mmti.mmid, fs.format('witness-type','non-MMGen') - assert mmti.mmid.mmtype == ('S','B')[ti['scriptSig']==b''],( + assert mmti.mmid.mmtype == ('S','B')[ti['scriptSig']==''],( fs.format('witness-type',mmti.mmid.mmtype)) else: # non-witness if mmti.mmid: @@ -936,7 +935,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam if bogus_send: m = 'BOGUS transaction NOT sent: {}' else: - assert ret.encode() == self.coin_txid, 'txid mismatch (after sending)' + assert ret == self.coin_txid, 'txid mismatch (after sending)' m = 'Transaction sent: {}' self.desc = 'sent transaction' msg(m.format(self.coin_txid.hl())) @@ -1034,7 +1033,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam ((n+1,'')[ip],'address:',e.addr.fmt(color=True,width=addr_w) + ' '+mmid_fmt), ('','comment:',e.label.hl() if e.label else ''), ('','amount:','{} {}'.format(e.amt.hl(),g.dcoin))] - items = [(n+1, 'tx,vout:','{},{}'.format(e.txid.decode(),e.vout))] + icommon + [ + items = [(n+1, 'tx,vout:','{},{}'.format(e.txid,e.vout))] + icommon + [ ('','confirmations:','{} (around {} days)'.format(confs,days) if blockcount else '') ] if ip else icommon + [ ('','change:',green('True') if e.is_chg else '')] @@ -1110,7 +1109,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam return out # TX label might contain non-ascii chars def check_txfile_hex_data(self): - self.hex = HexBytes(self.hex,on_fail='raise') + self.hex = HexStr(self.hex,on_fail='raise') def parse_tx_file(self,infile,metadata_only=False,silent_open=False): diff --git a/mmgen/util.py b/mmgen/util.py index 137f6cc2..6dfe0391 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -22,7 +22,6 @@ 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,digits from mmgen.color import * from mmgen.exception import * @@ -260,8 +259,6 @@ def is_int(s): # https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet # https://tools.ietf.org/html/rfc4648 -def is_hex_bytes(s): return set(list(s.lower())) <= set(list(hexdigits.lower().encode())) - def is_hex_str(s): return set(list(s.lower())) <= set(list(hexdigits.lower())) def is_hex_str_lc(s): return set(list(s)) <= set(list(hexdigits.lower())) def is_hex_str_uc(s): return set(list(s)) <= set(list(hexdigits.upper())) @@ -298,12 +295,12 @@ class baseconv(object): @classmethod def b58encode(cls,s,pad=None): pad = cls.get_pad(s,pad,'en',cls.b58pad_lens,[bytes]) - return cls.fromhex(hexlify(s),'b58',pad=pad,tostr=True) + return cls.fromhex(s.hex(),'b58',pad=pad,tostr=True) @classmethod def b58decode(cls,s,pad=None): pad = cls.get_pad(s,pad,'de',cls.b58pad_lens_rev,[bytes,str]) - return unhexlify(cls.tohex(s,'b58',pad=pad*2 if pad else None)) + return bytes.fromhex(cls.tohex(s,'b58',pad=pad*2 if pad else None)) @staticmethod def get_pad(s,pad,op,pad_map,ok_types): @@ -353,13 +350,13 @@ class baseconv(object): deconv = [wl.index(words[::-1][i])*(base**i) for i in range(len(words))] ret = ('{:0{w}x}'.format(sum(deconv),w=pad or 0)) - return (('','0')[len(ret) % 2] + ret).encode() # return bytes, for consistency with hexlify() + return (('','0')[len(ret) % 2] + ret) @classmethod def fromhex(cls,hexnum,wl_id,pad=None,tostr=False): - if not is_hex_str(hexnum.decode()): - die(2,"'{}': not a hexadecimal number".format(hexnum.decode())) + if not is_hex_str(hexnum): + die(2,"{!r}: not a hexadecimal number".format(hexnum)) wl = cls.digits[wl_id] base = len(wl) @@ -411,7 +408,7 @@ def pretty_hexdump(data,gw=2,cols=8,line_nums=False): return ''.join( [ ('' if (line_nums == False or i % cols) else '{:06x}: '.format(i*gw)) + - hexlify(data[i*gw:i*gw+gw]).decode() + ('\n',' ')[bool((i+1) % cols)] + data[i*gw:i*gw+gw].hex() + ('\n',' ')[bool((i+1) % cols)] for i in range(len(data)//gw + r) ] ).rstrip() + '\n' @@ -421,7 +418,7 @@ def decode_pretty_hexdump(data): pat = r'^[{}]+:\s+'.format(hexdigits) lines = [re.sub(pat,'',l) for l in data.splitlines()] try: - return unhexlify(''.join((''.join(lines).split()))) + return bytes.fromhex(''.join((''.join(lines).split()))) except: msg('Data not in hexdump format') return False diff --git a/test/common.py b/test/common.py index 0736335c..1eb8ab90 100755 --- a/test/common.py +++ b/test/common.py @@ -29,14 +29,13 @@ class TestSuiteException(Exception): pass class TestSuiteFatalException(Exception): pass import os -from binascii import hexlify from mmgen.common import * -def getrandnum(n): return int(hexlify(os.urandom(n)),16) -def getrandhex(n): return hexlify(os.urandom(n)).decode() +def getrandnum(n): return int(os.urandom(n).hex(),16) +def getrandhex(n): return os.urandom(n).hex() def getrandnum_range(nbytes,rn_max): while True: - rn = int(hexlify(os.urandom(nbytes)),16) + rn = int(os.urandom(nbytes).hex(),16) if rn < rn_max: return rn def getrandstr(num_chars,no_space=False): diff --git a/test/gentest.py b/test/gentest.py index 6c757d13..7a7c3696 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -26,8 +26,6 @@ os.chdir(os.path.join(pn,os.pardir)) sys.path.__setitem__(0,os.path.abspath(os.curdir)) os.environ['MMGEN_TEST_SUITE'] = '1' -from binascii import hexlify - # Import these _after_ local path's been added to sys.path from mmgen.common import * from mmgen.obj import MMGenAddrType @@ -81,7 +79,7 @@ if not 1 <= len(cmd_args) <= 2: opts.usage() addr_type = MMGenAddrType(opt.type or g.proto.dfl_mmtype) def pyethereum_sec2addr(sec): - return sec.decode(),hexlify(eth.privtoaddr(sec.decode())).decode() + return sec,eth.privtoaddr(sec).hex() def keyconv_sec2addr(sec): p = sp.Popen(['keyconv','-C',g.coin,sec.wif],stderr=sp.PIPE,stdout=sp.PIPE) @@ -95,7 +93,7 @@ def zcash_mini_sec2addr(sec): def pycoin_sec2addr(sec): coin = ci.external_tests['testnet']['pycoin'][g.coin] if g.testnet else g.coin - key = pcku.parse_key(sec.decode(),[network_for_netcode(coin)])[1] + key = pcku.parse_key(sec,[network_for_netcode(coin)])[1] if key is None: die(1,"can't parse {}".format(sec)) d = { 'legacy': ('wif_uncompressed','address_uncompressed'), @@ -205,7 +203,7 @@ def speed_test(): from struct import pack,unpack seed = os.urandom(28) print('Incrementing key with each round') - print('Starting key:', hexlify(seed+pack('I',0))) + print('Starting key:', (seed + pack('I',0)).hex()) import time start = last_t = time.time() diff --git a/test/objtest.py b/test/objtest.py index 5a6fb0fd..ef78bb0f 100755 --- a/test/objtest.py +++ b/test/objtest.py @@ -25,8 +25,6 @@ pn = os.path.dirname(sys.argv[0]) os.chdir(os.path.join(pn,os.pardir)) sys.path.__setitem__(0,os.path.abspath(os.curdir)) -from binascii import hexlify - # Import these _after_ local path's been added to sys.path from mmgen.common import * from mmgen.obj import * @@ -162,20 +160,16 @@ tests = OrderedDict([ 'F00BAA12:S:9999999 comment', tw_pfx+'x comment') }), - ('HexBytes', { - 'bad': (1,[],'\0','\1','я','g','gg','FF','f00'), - 'good': ('deadbeef','f00baa12') - }), ('MMGenTxID', { 'bad': (1,[],'\0','\1','я','g','gg','FF','f00','F00F0012'), 'good': ('DEADBE','F00BAA') }), ('CoinTxID',{ - 'bad': (1,[],'\0','\1','я','g','gg','FF','f00','F00F0012',hexlify(r16),hexlify(r32)+b'ee'), - 'good': (hexlify(r32),) + 'bad': (1,[],'\0','\1','я','g','gg','FF','f00','F00F0012',r16.hex(),r32.hex()+'ee'), + 'good': (r32.hex(),) }), ('WifKey', { - 'bad': (1,[],'\0','\1','я','g','gg','FF','f00',hexlify(r16),'2MspvWFjBbkv2wzQGqhxJUYPCk3Y2jMaxLN'), + 'bad': (1,[],'\0','\1','я','g','gg','FF','f00',r16.hex(),'2MspvWFjBbkv2wzQGqhxJUYPCk3Y2jMaxLN'), 'good': { 'btc': (('5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb', 'KwWr9rDh8KK5TtDa3HLChEvQXNYcUXpwhRFUPc5uSNnMtqNKLFhk'), @@ -211,8 +205,8 @@ tests = OrderedDict([ {'wif':'cSaJAXBAm9ooHpVJgoxqjDG3AcareFy29Cz8mhnNTRijjv2HLgta', 'ret':'94fa8b90c11fea8fb907c9376b919534b0a75b9a9621edf71a78753544b4101c'})), }[g.coin.lower()][bool(g.testnet)], - {'s':r32,'compressed':False,'pubkey_type':'std','ret':hexlify(r32)}, - {'s':r32,'compressed':True,'pubkey_type':'std','ret':hexlify(r32)} + {'s':r32,'compressed':False,'pubkey_type':'std','ret':r32.hex()}, + {'s':r32,'compressed':True,'pubkey_type':'std','ret':r32.hex()} ) }), ('AddrListID', { # a rather pointless test, but do it anyway diff --git a/test/sha2test.py b/test/sha2test.py index 9a0a3b3e..e1d8313f 100755 --- a/test/sha2test.py +++ b/test/sha2test.py @@ -20,7 +20,6 @@ test/sha2test.py: Test MMGen's SHA256 and SHA512 implementations """ import sys,os -from binascii import hexlify from mmgen.sha2 import Sha256,Sha512 from mmgen.util import die @@ -47,10 +46,11 @@ class TestSha2(object): def compare_hashes(self,dlen,data): import hashlib - sha2 = getattr(hashlib,self.desc)(data).hexdigest().encode() - my_sha2 = self.t_cls(data).hexdigest() - if my_sha2 != sha2: - die(3,'Hashes do not match!\nmy_sha2: {}\nsha2: {}\n'.format(my_sha2,sha2)) + sha2_ref = getattr(hashlib,self.desc)(data).hexdigest() + ret = self.t_cls(data).hexdigest() + if ret != sha2_ref: + m ='\nHashes do not match!\nReference {d}: {}\nMMGen {d}: {}' + die(3,m.format(sha2_ref,ret,d=self.desc.upper())) def test_ref(self,input_n=None): @@ -77,7 +77,7 @@ class TestSha2(object): for i in range(rounds): if i+1 in (1,rounds) or not (i+1) % 10: msg('\rTesting random input data: {:4}/{} '.format(i+1,rounds)) - dlen = int(hexlify(os.urandom(4)),16) >> 18 + dlen = int(os.urandom(4).hex(),16) >> 18 self.compare_hashes(dlen,os.urandom(dlen)) msg('OK\n') diff --git a/test/test.py b/test/test.py index 1050367e..0bddb48a 100755 --- a/test/test.py +++ b/test/test.py @@ -360,7 +360,7 @@ def list_cmds(): def do_between(): if opt.pause: confirm_continue() - elif opt.verbose or opt.exact_output: + elif (opt.verbose or opt.exact_output) and not opt.skip_deps: sys.stderr.write('\n') def list_tmpdirs(): @@ -747,7 +747,6 @@ class TestSuiteRunner(object): # delete files depended on by this cmd arg_list = [get_file_with_ext(cfgs[num]['tmpdir'],ext) for num,ext in d] - if opt.resume: if cmd == opt.resume: bmsg('Resuming at {!r}'.format(cmd)) diff --git a/test/test_py_d/common.py b/test/test_py_d/common.py index 2e338479..3dcb9eda 100755 --- a/test/test_py_d/common.py +++ b/test/test_py_d/common.py @@ -104,7 +104,7 @@ def end_silence(): g.stdout_fileno = 1 def randbool(): - return hexlify(os.urandom(1))[1] in b'12345678' + return os.urandom(1).hex()[0] in '13579ace' def disable_debug(): global save_debug diff --git a/test/test_py_d/ts_autosign.py b/test/test_py_d/ts_autosign.py index 609ab032..6e6ba285 100755 --- a/test/test_py_d/ts_autosign.py +++ b/test/test_py_d/ts_autosign.py @@ -73,7 +73,7 @@ class TestSuiteAutosign(TestSuiteBase): def get_pad_chars(n): ret = '' for i in range(n): - m = int(hexlify(os.urandom(1)),16) % 32 + m = int(os.urandom(1).hex(),16) % 32 ret += r'123579!@#$%^&*()_+-=[]{}"?/,.<>|'[m] return ret diff --git a/test/test_py_d/ts_ethdev.py b/test/test_py_d/ts_ethdev.py index 753577f6..30122f49 100755 --- a/test/test_py_d/ts_ethdev.py +++ b/test/test_py_d/ts_ethdev.py @@ -524,7 +524,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): txid = self.txsend_ui_common(t,caller=mmgen_cmd,quiet=True,bogus_send=False) addr = t.expect_getend('Contract address: ') from mmgen.altcoins.eth.tx import EthereumMMGenTX as etx - assert etx.get_exec_status(txid.encode(),True) != 0,( + assert etx.get_exec_status(txid,True) != 0,( "Contract '{}:{}' failed to execute. Aborting".format(num,key)) if key == 'Token': self.write_to_tmpfile('token_addr{}'.format(num),addr+'\n') diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index 19d59daf..f101b924 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -258,17 +258,17 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): if not segwit and k == 'p2sh': k = 'p2pkh' s_beg,s_end = { 'p2pkh': ('76a914','88ac'), 'p2sh': ('a914','87'), - 'bech32': (g.proto.witness_vernum_hex.decode()+'14','') }[k] + 'bech32': (g.proto.witness_vernum_hex + '14','') }[k] amt1,amt2 = {'btc':(10,40),'bch':(10,40),'ltc':(1000,4000)}[g.coin.lower()] ret = { self.lbl_id: '{}:{}'.format(g.proto.base_coin.lower(),coinaddr) if non_mmgen \ else ('{}:{}{}'.format(al_id,idx,lbl)), 'vout': int(getrandnum(4) % 8), - 'txid': hexlify(os.urandom(32)), + 'txid': os.urandom(32).hex(), 'amount': g.proto.coin_amt('{}.{}'.format(amt1 + getrandnum(4) % amt2, getrandnum(4) % 100000000)), 'address': coinaddr, 'spendable': False, - 'scriptPubKey': '{}{}{}'.format(s_beg,coinaddr.hex.decode(),s_end).encode(), + 'scriptPubKey': '{}{}{}'.format(s_beg,coinaddr.hex,s_end), 'confirmations': getrandnum(3) // 2 # max: 8388608 (7 digits) } return ret diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index eafbe266..c97129ed 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -530,7 +530,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): return self.alice_add_label_badaddr(rt_pw,'Invalid coin address for this chain: '+rt_pw) def alice_add_label_badaddr2(self): - addr = g.proto.pubhash2addr(b'00'*20,False) # mainnet zero address + addr = g.proto.pubhash2addr('00'*20,False) # mainnet zero address return self.alice_add_label_badaddr(addr,'Invalid coin address for this chain: '+addr) def alice_add_label_badaddr3(self): @@ -539,7 +539,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): "MMGen address '{}' not found in tracking wallet".format(addr)) def alice_add_label_badaddr4(self): - addr = CoinProtocol(g.coin,True).pubhash2addr(b'00'*20,False) # testnet zero address + addr = CoinProtocol(g.coin,True).pubhash2addr('00'*20,False) # testnet zero address return self.alice_add_label_badaddr(addr, "Address '{}' not found in tracking wallet".format(addr))