From 3e0caf7beb4214f3babe773efa53ccf1e9a5a726 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 2 Nov 2019 10:39:30 +0000 Subject: [PATCH] _b58chk_encode(), _b58chk_decode(): reimplement using bytes --- mmgen/addr.py | 6 ++++-- mmgen/altcoin.py | 8 +++++--- mmgen/protocol.py | 41 +++++++++++++++++++++++------------------ mmgen/tool.py | 4 ++-- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/mmgen/addr.py b/mmgen/addr.py index 86325663..5c363c96 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -123,7 +123,8 @@ class AddrGeneratorZcashZ(AddrGenerator): 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] + (self.zhash256(key,0)+p2).hex()) + ver_bytes = bytes.fromhex(g.proto.addr_ver_num['zcash_z'][0]) + ret = _b58chk_encode(ver_bytes + self.zhash256(key,0) + p2) assert len(ret) == self.addr_width,'Invalid Zcash z-address length' return CoinAddr(ret) @@ -135,7 +136,8 @@ class AddrGeneratorZcashZ(AddrGenerator): vk[63] &= 0x7f vk[63] |= 0x40 from mmgen.protocol import _b58chk_encode - ret = _b58chk_encode(g.proto.addr_ver_num['viewkey'][0] + vk.hex()) + ver_bytes = bytes.fromhex(g.proto.addr_ver_num['viewkey'][0]) + ret = _b58chk_encode(ver_bytes + vk) assert len(ret) == self.vk_width,'Invalid Zcash view key length' return ZcashViewKey(ret) diff --git a/mmgen/altcoin.py b/mmgen/altcoin.py index 19fa4a91..90d1044a 100755 --- a/mmgen/altcoin.py +++ b/mmgen/altcoin.py @@ -458,10 +458,12 @@ class CoinInfo(object): def phash2addr(ver_num,pk_hash): from mmgen.protocol import _b58chk_encode - return _b58chk_encode('{:0{}x}'.format(ver_num,2 if ver_num < 256 else 4) + pk_hash) + bl = ver_num.bit_length() + ver_bytes = int.to_bytes(ver_num,bl//8 + bool(bl%8),'big') + return _b58chk_encode(ver_bytes + pk_hash) - low = phash2addr(ver_num,'00'*20) - high = phash2addr(ver_num,'ff'*20) + low = phash2addr(ver_num,b'\x00'*20) + high = phash2addr(ver_num,b'\xff'*20) if verbose: print('low address: ' + low) diff --git a/mmgen/protocol.py b/mmgen/protocol.py index 5f428fb8..cb3a05fc 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -37,6 +37,9 @@ def hash160(hexnum): # take hex, return hex - OP_HASH160 def hash256(hexnum): # take hex, return hex - OP_HASH256 return hashlib.sha256(hashlib.sha256(bytes.fromhex(hexnum)).digest()).hexdigest() +def hash256bytes(bstr): # bytes in, bytes out - OP_HASH256 + return hashlib.sha256(hashlib.sha256(bstr).digest()).digest() + _b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # From en.bitcoin.it: @@ -46,24 +49,22 @@ _b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # The 'zero address': # 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20) -def _b58chk_encode(hexstr): - lzeroes = (len(hexstr) - len(hexstr.lstrip('0'))) // 2 - def b58enc(n): +def _b58chk_encode(bstr): + lzeroes = len(bstr) - len(bstr.lstrip(b'\x00')) + def do_enc(n): while n: yield _b58a[n % 58] n //= 58 - return ('1' * lzeroes) + ''.join(b58enc(int(hexstr+hash256(hexstr)[:8],16)))[::-1] + return ('1' * lzeroes) + ''.join(do_enc(int.from_bytes(bstr+hash256bytes(bstr)[:4],'big')))[::-1] 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])) ) - if len(hexstr) % 2: hexstr = '0' + hexstr - if hexstr[-8:] != hash256(hexstr[:-8])[:8]: - fs = '_b58chk_decode(): {}: incorrect checksum for {!r}, expected {}' - raise ValueError(fs.format(hexstr[-8:],hexstr[:-8],hash256(hexstr[:-8])[:8])) - return hexstr[:-8] + res = sum(_b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1])) + bl = res.bit_length() + out = b'\x00' * lzeroes + res.to_bytes(bl//8 + bool(bl%8),'big') + if out[-4:] != hash256bytes(out[:-4])[:4]: + raise ValueError('_b58chk_decode(): incorrect checksum') + return out[:-4] # chainparams.cpp class BitcoinProtocol(MMGenObject): @@ -129,14 +130,18 @@ class BitcoinProtocol(MMGenObject): return (pk % cls.secp256k1_ge).to_bytes(cls.privkey_len,'big') @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) + def hex2wif(cls,hexpriv,pubkey_type,compressed): # input is preprocessed hex + sec = bytes.fromhex(hexpriv) + assert len(sec) == cls.privkey_len, '{} bytes: incorrect private key length!'.format(len(sec)) assert pubkey_type in cls.wif_ver_num, '{!r}: invalid pubkey_type'.format(pubkey_type) - return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + ('','01')[bool(compressed)]) + return _b58chk_encode( + bytes.fromhex(cls.wif_ver_num[pubkey_type]) + + sec + + (b'',b'\x01')[bool(compressed)]) @classmethod def parse_wif(cls,wif): - key = bytes.fromhex(_b58chk_decode(wif)) + key = _b58chk_decode(wif) for k,v in cls.wif_ver_num.items(): v = bytes.fromhex(v) @@ -176,7 +181,7 @@ class BitcoinProtocol(MMGenObject): if type(pfx) == tuple: if addr[0] not in pfx: continue elif addr[:len(pfx)] != pfx: continue - addr_hex = _b58chk_decode(addr) + addr_hex = _b58chk_decode(addr).hex() if addr_hex[:len(ver_num)] != ver_num: continue return { 'hex': addr_hex[len(ver_num):], @@ -193,7 +198,7 @@ class BitcoinProtocol(MMGenObject): def pubhash2addr(cls,pubkey_hash,p2sh): assert len(pubkey_hash) == 40,'{}: invalid length for pubkey hash'.format(len(pubkey_hash)) s = cls.addr_ver_num[('p2pkh','p2sh')[p2sh]][0] + pubkey_hash - return _b58chk_encode(s) + return _b58chk_encode(bytes.fromhex(s)) # Segwit: @classmethod diff --git a/mmgen/tool.py b/mmgen/tool.py index e6abd5cb..21f9a299 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -340,12 +340,12 @@ 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) + return _b58chk_encode(bytes.fromhex(hexstr)) def b58chktohex(self,b58chk_num:'sstr'): "convert a base58-check encoded number to hexadecimal" from mmgen.protocol import _b58chk_decode - return _b58chk_decode(b58chk_num) + return _b58chk_decode(b58chk_num).hex() def hextob32(self,hexstr:'sstr',pad=0): "convert a hexadecimal number to MMGen's flavor of base 32"