Browse Source

reimplement base58 routines, move from bitcoin module to class baseconv

philemon 7 years ago
parent
commit
33673cbb1b
5 changed files with 52 additions and 82 deletions
  1. 17 68
      mmgen/bitcoin.py
  2. 6 7
      mmgen/seed.py
  3. 2 2
      mmgen/tool.py
  4. 3 5
      mmgen/tx.py
  5. 24 0
      mmgen/util.py

+ 17 - 68
mmgen/bitcoin.py

@@ -39,17 +39,26 @@ _generator_secp256k1 = ecdsa.ellipticcurve.Point(_curve_secp256k1,_Gx,_Gy,_r)
 _oid_secp256k1 = (1,3,132,0,10)
 _secp256k1 = ecdsa.curves.Curve('secp256k1',_curve_secp256k1,_generator_secp256k1,_oid_secp256k1)
 
-b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
-
 # From en.bitcoin.it:
-#   The Base58 encoding used is home made, and has some differences.
-#   Especially, leading zeroes are kept as single zeroes when conversion
-#   happens.
-#
+#  The Base58 encoding used is home made, and has some differences.
+#  Especially, leading zeroes are kept as single zeroes when conversion happens.
 # Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
-#
 # The 'zero address':
-# 1111111111111111111114oLvT2 (use step2 = ('0' * 40) to generate)
+# 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20)
+_b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+
+def _numtob58(num):
+	ret = []
+	while num:
+		ret.append(_b58a[num % 58])
+		num /= 58
+	return ''.join(ret)[::-1]
+
+def _b58tonum(b58num):
+	b58num = b58num.strip()
+	for i in b58num:
+		if not i in _b58a: return False
+	return sum(_b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1])))
 
 from mmgen.globalvars import g
 
@@ -91,66 +100,6 @@ def verify_addr(addr,verbose=False,return_hex=False,return_type=False):
 	if verbose: Msg("Invalid address '%s'" % addr)
 	return False
 
-# Reworked code from here:
-
-def _numtob58(num):
-	ret = []
-	while num:
-		ret.append(b58a[num % 58])
-		num /= 58
-	return ''.join(ret)[::-1]
-
-def _b58tonum(b58num):
-	b58num = b58num.strip()
-	for i in b58num:
-		if not i in b58a: return False
-	return sum(b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1])))
-
-# The following are MMGen internal (non-Bitcoin) b58 functions
-
-# Drop-in replacements for b64encode() and b64decode():
-# (well, not exactly: they yield numeric but not bytewise equivalence)
-
-def b58encode(s):
-	if s == '': return ''
-	num = int(hexlify(s),16)
-	return _numtob58(num)
-
-def b58decode(b58num):
-	b58num = b58num.strip()
-	if b58num == '': return ''
-	# Zap all spaces:
-	# Use translate() only with str, not unicode
-	num = _b58tonum(str(b58num).translate(None,' \t\n\r'))
-	if num == False: return False
-	out = u'{:x}'.format(num)
-	return unhexlify(u'0'*(len(out)%2) + out)
-
-# These yield bytewise equivalence in our special cases:
-
-bin_lens = 16,24,32
-b58_lens = 22,33,44
-
-def _b58_pad(s,a,b,pad,f,w):
-	try:
-		outlen = b[a.index(len(s))]
-	except:
-		Msg('_b58_pad() accepts only %s %s bytes long '\
-			'(input was %s bytes)' % (w,','.join([str(i) for i in a]),len(s)))
-		return False
-
-	out = f(s)
-	if out == False: return False
-	return '%s%s' % (pad * (outlen - len(out)), out)
-
-def b58encode_pad(s):
-	return _b58_pad(s,
-		a=bin_lens,b=b58_lens,pad='1',f=b58encode,w='binary strings')
-
-def b58decode_pad(s):
-	return _b58_pad(s,
-		a=b58_lens,b=bin_lens,pad='\0',f=b58decode,w='base 58 numbers')
-
 # Compressed address support:
 
 def wif_is_compressed(wif): return wif[0] != ('5','9')[g.testnet]

+ 6 - 7
mmgen/seed.py

@@ -24,7 +24,6 @@ import os
 from binascii import hexlify,unhexlify
 
 from mmgen.common import *
-from mmgen.bitcoin import b58encode_pad,b58decode_pad,b58_lens
 from mmgen.obj import *
 from mmgen.filename import *
 from mmgen.crypto import *
@@ -478,7 +477,7 @@ class SeedFile (SeedSourceUnenc):
 	ext = 'mmseed'
 
 	def _format(self):
-		b58seed = b58encode_pad(self.seed.data)
+		b58seed = baseconv.b58encode(self.seed.data,pad=True)
 		self.ssdata.chksum = make_chksum_6(b58seed)
 		self.ssdata.b58seed = b58seed
 		self.fmt_data = '%s %s\n' % (
@@ -509,7 +508,7 @@ class SeedFile (SeedSourceUnenc):
 		if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
 			return False
 
-		ret = b58decode_pad(b)
+		ret = baseconv.b58decode(b,pad=True)
 
 		if ret == False:
 			msg('Invalid base-58 encoded seed: %s' % val)
@@ -626,8 +625,8 @@ class Wallet (SeedSourceEnc):
 	def _format(self):
 		d = self.ssdata
 		s = self.seed
-		slt_fmt  = b58encode_pad(d.salt)
-		es_fmt = b58encode_pad(d.enc_seed)
+		slt_fmt  = baseconv.b58encode(d.salt,pad=True)
+		es_fmt = baseconv.b58encode(d.enc_seed,pad=True)
 		lines = (
 			d.label,
 			'{} {} {} {} {}'.format(s.sid.lower(), d.key_id.lower(),
@@ -688,7 +687,7 @@ class Wallet (SeedSourceEnc):
 					(' '.join(hash_params), d.hash_preset))
 			return False
 
-		lmin,lmax = b58_lens[0],b58_lens[-1] # 22,33,44
+		lmin,foo,lmax = [v for k,v in baseconv.b58pad_lens] # 22,33,44
 		for i,key in (4,'salt'),(5,'enc_seed'):
 			l = lines[i].split(' ')
 			chk = l.pop(0)
@@ -702,7 +701,7 @@ class Wallet (SeedSourceEnc):
 					make_chksum_6(b58_val),'computed checksum',verbose=True):
 				return False
 
-			val = b58decode_pad(b58_val)
+			val = baseconv.b58decode(b58_val,pad=True)
 			if val == False:
 				msg('Invalid base 58 number: %s' % b58_val)
 				return False

+ 2 - 2
mmgen/tool.py

@@ -322,8 +322,8 @@ def Unhexdump(infile):
 
 def B58randenc():
 	r = get_random(32)
-	enc = mmb.b58encode(r)
-	dec = mmb.b58decode(enc)
+	enc = baseconv.b58encode(r,pad=True)
+	dec = baseconv.b58decode(enc,pad=True)
 	print_convert_results(r,enc,dec,'str')
 
 def Randhex(nbytes='32'):

+ 3 - 5
mmgen/tx.py

@@ -406,7 +406,6 @@ class MMGenTX(MMGenObject):
 		self.blockcount = int(c.getblockcount())
 
 	def format(self):
-		from mmgen.bitcoin import b58encode
 		lines = [
 			'{} {} {} {} {}'.format(
 				self.chain.upper() if self.chain else 'Unknown',
@@ -420,7 +419,7 @@ class MMGenTX(MMGenObject):
 			repr([e.__dict__ for e in self.outputs])
 		]
 		if self.label:
-			lines.append(b58encode(self.label.encode('utf8')))
+			lines.append(baseconv.b58encode(self.label.encode('utf8')))
 		if self.btc_txid:
 			if not self.label: lines.append('-') # keep old tx files backwards compatible
 			lines.append(self.btc_txid)
@@ -761,8 +760,7 @@ class MMGenTX(MMGenObject):
 		if len(tx_data) == 5:
 			c = tx_data.pop(-1)
 			if c != '-':
-				from mmgen.bitcoin import b58decode
-				comment = b58decode(c)
+				comment = baseconv.b58decode(c).decode('utf8')
 				if comment == False:
 					do_err('encoded comment (not base58)')
 				else:
@@ -770,7 +768,7 @@ class MMGenTX(MMGenObject):
 					if not self.label:
 						do_err('comment')
 		else:
-			comment = ''
+			comment = u''
 
 		if len(tx_data) == 4:
 			metadata,self.hex,inputs_data,outputs_data = tx_data

+ 24 - 0
mmgen/util.py

@@ -253,6 +253,30 @@ class baseconv(object):
 		'tirosh':   '48f05e1f', # tirosh truncated to mn_base (1626)
 		# 'tirosh1633': '1a5faeff'
 	}
+	b58pad_lens =     [(16,22), (24,33), (32,44)]
+	b58pad_lens_rev = [(v,k) for k,v in b58pad_lens]
+
+	@classmethod
+	def b58encode(cls,s,pad=None):
+		pad = cls.get_pad(s,pad,'en',cls.b58pad_lens,[bytes])
+		return ''.join(cls.fromhex(hexlify(s),'b58',pad=pad))
+
+	@classmethod
+	def b58decode(cls,s,pad=None):
+		pad = cls.get_pad(s,pad,'de',cls.b58pad_lens_rev,[bytes,unicode])
+		return unhexlify(cls.tohex(s,'b58',pad=pad*2 if pad else None))
+
+	@staticmethod
+	def get_pad(s,pad,op,pad_map,ok_types):
+		m = "b58{}code() input must be one of {}, not '{}'"
+		assert type(s) in ok_types, m.format(op,repr([t.__name__ for t in ok_types]),type(s).__name__)
+		if pad:
+			assert type(pad) == bool, "'pad' must be boolean type"
+			d = dict(pad_map)
+			assert len(s) in d, 'Invalid data length for b58{}code(pad=True)'.format(op)
+			return d[len(s)]
+		else:
+			return None
 
 	@classmethod
 	def get_wordlist_chksum(cls,wl_id):