reimplement base58 routines, move from bitcoin module to class baseconv
This commit is contained in:
parent
2cfa35e8a1
commit
33673cbb1b
5 changed files with 52 additions and 82 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue