Browse Source

_b58chk_encode(), _b58chk_decode(): reimplement using bytes

The MMGen Project 5 years ago
parent
commit
3e0caf7beb
4 changed files with 34 additions and 25 deletions
  1. 4 2
      mmgen/addr.py
  2. 5 3
      mmgen/altcoin.py
  3. 23 18
      mmgen/protocol.py
  4. 2 2
      mmgen/tool.py

+ 4 - 2
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)
 

+ 5 - 3
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)

+ 23 - 18
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

+ 2 - 2
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"