Zcash z-address generation with custom sha256 implementation

- Tests have been updated, test with `scripts/test-release.sh -Pn master alts`
This commit is contained in:
MMGen 2017-12-16 12:48:47 +03:00
commit 1cc64e1eb9
Signed by untrusted user who does not match committer: mmgen
GPG key ID: 62DBE9E5212F05BE
15 changed files with 439 additions and 156 deletions

View file

@ -32,14 +32,15 @@ def sc_dmsg(desc,data):
Msg('sc_debug_{}: {}'.format(desc,data))
class AddrGenerator(MMGenObject):
def __new__(cls,atype):
def __new__(cls,gen_method):
d = {
'p2pkh': AddrGeneratorP2PKH,
'segwit': AddrGeneratorSegwit,
'ethereum': AddrGeneratorEthereum
'ethereum': AddrGeneratorEthereum,
'zcash_z': AddrGeneratorZcashZ
}
assert atype in d
me = super(cls,cls).__new__(d[atype])
assert gen_method in d
me = super(cls,cls).__new__(d[gen_method])
me.desc = d
return me
@ -70,15 +71,49 @@ class AddrGeneratorEthereum(AddrGenerator):
def to_segwit_redeem_script(self,pubhex):
raise NotImplementedError
class AddrGeneratorZcashZ(AddrGenerator):
def zhash256(self,vhex,t):
byte0 = '{:02x}'.format(int(vhex[:2],16) | 0xc0)
byte32 = '{:02x}'.format(t)
vhex_fix = byte0 + vhex[2:64] + byte32 + '00' * 31
assert len(vhex_fix) == 128
from mmgen.sha256 import Sha256
return Sha256(unhexlify(vhex_fix),preprocess=False).hexdigest()
def to_addr(self,pubhex): # pubhex is really privhex
key = pubhex
assert len(key) == 64,'{}: incorrect privkey length'.format(len(key))
addr1 = self.zhash256(key,0)
addr2 = self.zhash256(key,1)
from nacl.bindings import crypto_scalarmult_base
addr2 = hexlify(crypto_scalarmult_base(unhexlify(addr2)))
from mmgen.protocol import _b58chk_encode
ret = _b58chk_encode(g.proto.addr_ver_num['zcash_z'][0] + addr1 + addr2)
assert len(ret) == g.proto.addr_width,'Invalid zaddr length'
return CoinAddr(ret)
def to_segwit_redeem_script(self,pubhex):
raise NotImplementedError
class KeyGenerator(MMGenObject):
def __new__(cls,generator=None,silent=False):
if cls.test_for_secp256k1(silent=silent) and generator != 1:
if not opt.key_generator or opt.key_generator == 2 or generator == 2:
return super(cls,cls).__new__(KeyGeneratorSecp256k1)
def __new__(cls,pubkey_type,generator=None,silent=False):
if pubkey_type == 'std':
if cls.test_for_secp256k1(silent=silent) and generator != 1:
if not opt.key_generator or opt.key_generator == 2 or generator == 2:
return super(cls,cls).__new__(KeyGeneratorSecp256k1)
else:
msg('Using (slow) native Python ECDSA library for address generation')
return super(cls,cls).__new__(KeyGeneratorPython)
elif pubkey_type == 'zcash_z':
g.proto.addr_width = 95
me = super(cls,cls).__new__(KeyGeneratorDummy)
me.desc = 'mmgen-'+pubkey_type
return me
else:
msg('Using (slow) native Python ECDSA library for address generation')
return super(cls,cls).__new__(KeyGeneratorPython)
raise ValueError,'{}: invalid pubkey_type argument'.format(pubkey_type)
@classmethod
def test_for_secp256k1(self,silent=False):
@ -91,6 +126,7 @@ class KeyGenerator(MMGenObject):
import ecdsa
class KeyGeneratorPython(KeyGenerator):
desc = 'mmgen-python-ecdsa'
# From electrum:
# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
@ -119,19 +155,24 @@ class KeyGeneratorPython(KeyGenerator):
else:
return '04'+pubkey
desc = 'python-ecdsa'
def to_pubhex(self,privhex):
assert type(privhex) == PrivKey
return PubKey(self.privnum2pubhex(
int(privhex,16),compressed=privhex.compressed),compressed=privhex.compressed)
class KeyGeneratorSecp256k1(KeyGenerator):
desc = 'secp256k1'
desc = 'mmgen-secp256k1'
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)
class KeyGeneratorDummy(KeyGenerator):
desc = 'mmgen-dummy'
def to_pubhex(self,privhex):
assert type(privhex) == PrivKey
return PubKey(str(privhex),compressed=privhex.compressed)
class AddrListEntry(MMGenListItem):
addr = MMGenListItemAttr('addr','CoinAddr')
idx = MMGenListItemAttr('idx','AddrIdx') # not present in flat addrlists
@ -220,7 +261,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
self.update_msgs()
mmtype = mmtype or g.proto.dfl_mmtype
assert mmtype in MMGenAddrType.mmtypes
assert mmtype in MMGenAddrType.mmtypes,'{}: mmtype not in {}'.format(mmtype,repr(MMGenAddrType.mmtypes))
if seed and addr_idxs: # data from seed + idxs
self.al_id,src = AddrListID(seed.sid,mmtype),'gen'
@ -274,8 +315,11 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
seed = self.scramble_seed(seed)
sc_dmsg('seed',seed[:8].encode('hex'))
compressed = self.al_id.mmtype.compressed
pubkey_type = self.al_id.mmtype.pubkey_type
if self.gen_addrs:
kg = KeyGenerator()
kg = KeyGenerator(pubkey_type)
ag = AddrGenerator(self.al_id.mmtype.gen_method)
t_addrs,num,pos,out = len(addrnums),0,0,AddrListList()
@ -295,7 +339,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
e = le(idx=num)
# Secret key is double sha256 of seed hash round /num/
e.sec = PrivKey(sha256(sha256(seed).digest()).digest(),self.al_id.mmtype.compressed)
e.sec = PrivKey(sha256(sha256(seed).digest()).digest(),compressed=compressed,pubkey_type=pubkey_type)
if self.gen_addrs:
e.addr = ag.to_addr(kg.to_pubhex(e.sec))
@ -305,7 +349,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
dmsg('Key {:>03}: {}'.format(pos,e.passwd))
out.append(e)
if g.debug: print 'generate():\n', e.pformat()
if g.debug: Msg('generate():\n', e.pformat())
qmsg('\r%s: %s %s%s generated%s' % (
self.al_id.hl(),t_addrs,self.gen_desc,suf(t_addrs,self.gen_desc_pl),' '*15))
@ -398,7 +442,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
return [d.addr for d in self.data if not getattr(d,key)]
def generate_addrs_from_keys(self):
kg = KeyGenerator()
kg = KeyGenerator('std')
ag = AddrGenerator('p2pkh')
d = self.data
for n,e in enumerate(d,1):
@ -478,7 +522,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
ret.append(a)
if self.has_keys and keypress_confirm('Check key-to-address validity?'):
kg = KeyGenerator()
kg = KeyGenerator(self.al_id.mmtype.pubkey_type)
ag = AddrGenerator(self.al_id.mmtype.gen_method)
llen = len(ret)
for n,e in enumerate(ret):

View file

@ -38,8 +38,8 @@ class g(object):
# Constants:
version = '0.9.5'
release_date = 'November 2017'
version = '0.9.599'
release_date = 'December 2017'
proj_name = 'MMGen'
proj_url = 'https://github.com/mmgen/mmgen'
@ -47,7 +47,7 @@ class g(object):
author = 'Philemon'
email = '<mmgen@tuta.io>'
Cdates = '2013-2017'
keywords = 'Bitcoin, BTC, cryptocurrency, wallet, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MinGW, mswin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin'
keywords = 'Bitcoin, BTC, cryptocurrency, wallet, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MinGW, mswin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, DASH, ETH, Ethereum, Classic, SHA256Compress'
max_int = 0xffffffff
stdin_tty = bool(sys.stdin.isatty() or os.getenv('MMGEN_TEST_SUITE'))
http_timeout = 60

View file

@ -31,7 +31,7 @@ supported commands), use '-' as the first argument.
cmd_help = """
Cryptocoin address/key operations (compressed public keys supported):
addr2hexaddr - convert coin address from base58 to hex format
hex2wif - convert a private key from hex to WIF format
hex2wif - convert a private key from hex to WIF format (use 'pubkey_type=zcash_z' for zcash-z key)
pubhash2addr - convert public key hash to address
privhex2addr - generate coin address from private key in hex format
privhex2pubhex - generate a hex public key from a hex private key

View file

@ -538,9 +538,8 @@ class WifKey(str,Hilite,InitErrors):
try:
assert set(s) <= set(ascii_letters+digits),'not an ascii string'
from mmgen.globalvars import g
if g.proto.wif2hex(s):
return str.__new__(cls,s)
raise ValueError,'failed verification'
g.proto.wif2hex(s) # raises exception on error
return str.__new__(cls,s)
except Exception as e:
m = '{!r}: invalid value for WIF key ({})'.format(s,e[0])
return cls.init_fail(m,on_fail)
@ -566,7 +565,8 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
wif = MMGenImmutableAttr('wif',WifKey,typeconv=False)
# initialize with (priv_bin,compressed), WIF or self
def __new__(cls,s=None,compressed=None,wif=None,on_fail='die'):
def __new__(cls,s=None,compressed=None,wif=None,pubkey_type=None,on_fail='die'):
from mmgen.globalvars import g
if type(s) == cls: return s
assert wif or (s and type(compressed) == bool),'Incorrect args for PrivKey()'
@ -575,31 +575,28 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
if wif:
try:
assert set(wif) <= set(ascii_letters+digits),'not an ascii string'
from mmgen.globalvars import g
w2h = g.proto.wif2hex(wif)
assert w2h,"wif2hex() failed for wif key {!r}".format(wif)
w2h = g.proto.wif2hex(wif) # raises exception on error
me = str.__new__(cls,w2h['hex'])
me.compressed = w2h['compressed']
me.wif = str.__new__(WifKey,wif) # check has been done
me.pubkey_type = w2h['pubkey_type']
me.wif = str.__new__(WifKey,wif) # check has been done
return me
except Exception as e:
fs = "Value {!r} cannot be converted to WIF key ({})"
return cls.init_fail(fs.format(wif,e[0]),on_fail)
try:
from binascii import hexlify
assert len(s) == cls.width / 2,'Key length must be {}'.format(cls.width/2)
me = str.__new__(cls,hexlify(s))
me = str.__new__(cls,g.proto.preprocess_key(s.encode('hex'),pubkey_type))
me.compressed = compressed
me.wif = me.towif()
me.pubkey_type = pubkey_type
if me.pubkey_type: # skip WIF creation for passwds
me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise')
return me
except Exception as e:
fs = "Key={!r}\nCompressed={}\nValue pair cannot be converted to PrivKey\n({})"
return cls.init_fail(fs.format(s,compressed,e),on_fail)
def towif(self):
from mmgen.globalvars import g
return WifKey(g.proto.hex2wif(self,compressed=self.compressed),on_fail='raise')
class AddrListID(str,Hilite,InitErrors,MMGenObject):
width = 10
@ -674,25 +671,35 @@ class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
color = 'blue'
mmtypes = { # 'name' is used to cook the seed, so it must never change!
'L': { 'name':'legacy',
'comp':False,
'gen':'p2pkh',
'fmt':'p2pkh',
'pubkey_type':'std',
'compressed':False,
'gen_method':'p2pkh',
'addr_fmt':'p2pkh',
'desc':'Legacy uncompressed address'},
'C': { 'name':'compressed',
'comp':True,
'gen':'p2pkh',
'fmt':'p2pkh',
'pubkey_type':'std',
'compressed':True,
'gen_method':'p2pkh',
'addr_fmt':'p2pkh',
'desc':'Compressed P2PKH address'},
'S': { 'name':'segwit',
'comp':True,
'gen':'segwit',
'fmt':'p2sh',
'pubkey_type':'std',
'compressed':True,
'gen_method':'segwit',
'addr_fmt':'p2sh',
'desc':'Segwit P2SH-P2WPKH address' },
'E': { 'name':'ethereum',
'comp':False,
'gen':'ethereum',
'fmt':'ethereum',
'pubkey_type':'std',
'compressed':False,
'gen_method':'ethereum',
'addr_fmt':'ethereum',
'desc':'Ethereum address' },
'Z': { 'name':'zcash_z',
'pubkey_type':'zcash_z',
'compressed':False,
'gen_method':'zcash_z',
'addr_fmt':'zcash_z',
'desc':'Zcash z-address' },
}
def __new__(cls,s,on_fail='die',errmsg=None):
if type(s) == cls: return s
@ -705,11 +712,8 @@ class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
me = str.__new__(cls,s)
assert me in g.proto.mmtypes + ('P',), (
"'{}': invalid address type for {}".format(me,g.proto.__name__))
me.name = v['name']
me.compressed = v['comp']
me.gen_method = v['gen']
me.desc = v['desc']
me.addr_fmt = v['fmt']
for k in ('name','pubkey_type','compressed','gen_method','addr_fmt','desc'):
setattr(me,k,v[k])
return me
raise ValueError,'not found'
except Exception as e:
@ -724,8 +728,9 @@ class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
class MMGenPasswordType(MMGenAddrType):
mmtypes = {
'P': { 'name':'password',
'comp':False,
'gen':None,
'fmt':None,
'pubkey_type':None,
'compressed':False,
'gen_method':None,
'addr_fmt':None,
'desc':'Password generated from MMGen seed'}
}

View file

@ -50,15 +50,25 @@ def _numtob58(num):
def _b58tonum(b58num):
b58num = b58num.strip()
for i in b58num:
if not i in _b58a: return False
if not i in _b58a:
raise ValueError,'_b58tonum(): invalid b58 value'
return sum(_b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1])))
def _b58chk_encode(hexstr):
return _numtob58(int(hexstr+hash256(hexstr)[:8],16))
def _b58chk_decode(s):
hexstr = '{:x}'.format(_b58tonum(s))
if hexstr[-8:] == hash256(hexstr[:-8])[:8]:
return hexstr[:-8]
raise ValueError,'_b58chk_decode(): checksum incorrect'
# chainparams.cpp
class BitcoinProtocol(MMGenObject):
name = 'bitcoin'
daemon_name = 'bitcoind'
addr_ver_num = { 'p2pkh': ('00','1'), 'p2sh': ('05','3') }
wif_ver_num = '80'
wif_ver_num = { 'std': '80' }
mmtypes = ('L','C','S')
dfl_mmtype = 'L'
data_subdir = ''
@ -92,12 +102,31 @@ class BitcoinProtocol(MMGenObject):
def cap(cls,s): return s in cls.caps
@classmethod
def hex2wif(cls,hexpriv,compressed=False):
s = cls.wif_ver_num + hexpriv + ('','01')[bool(compressed)]
return _numtob58(int(s+hash256(s)[:8],16))
def preprocess_key(cls,hexpriv,pubkey_type): return hexpriv
@classmethod
def hex2wif(cls,hexpriv,pubkey_type,compressed):
return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + ('','01')[bool(compressed)])
@classmethod
def wif2hex(cls,wif):
key = _b58chk_decode(wif)
pubkey_type = None
for k,v in cls.wif_ver_num.items():
if key[:len(v)] == v:
pubkey_type = k
key = key[len(v):]
assert pubkey_type,'invalid WIF version number'
if len(key) == 66:
assert key[-2:] == '01','invalid compressed key suffix'
compressed = True
else:
assert len(key) == 64,'invalid key length'
compressed = False
return { 'hex':key[:64], 'pubkey_type':pubkey_type, 'compressed':compressed }
@classmethod
def wif2hex_old(cls,wif):
num = _b58tonum(wif)
if num == False: return False
key = '{:x}'.format(num)
@ -105,7 +134,7 @@ class BitcoinProtocol(MMGenObject):
compressed = len(key) == 76
if compressed and key[66:68] != '01': return False
klen = (66,68)[compressed]
if (key[:2] == cls.wif_ver_num and key[klen:] == hash256(key[:klen])[:8]):
if (key[:2] == cls.wif_ver_num['std'] and key[klen:] == hash256(key[:klen])[:8]):
return { 'hex':key[2:66], 'compressed':compressed }
else:
return False
@ -126,7 +155,7 @@ class BitcoinProtocol(MMGenObject):
if hash256(addr_hex[:-8])[:8] == addr_hex[-8:]:
return {
'hex': addr_hex[len(ver_num):-8],
'format': {'p2pkh':'p2pkh','p2sh':'p2sh','p2sh2':'p2sh'}[addr_fmt],
'format': {'p2pkh':'p2pkh','p2sh':'p2sh','p2sh2':'p2sh','zcash_z':'zcash_z'}[addr_fmt],
'width': cls.addr_width
} if return_dict else True
else:
@ -139,7 +168,7 @@ class BitcoinProtocol(MMGenObject):
def pubhash2addr(cls,pubkey_hash,p2sh):
s = cls.addr_ver_num[('p2pkh','p2sh')[p2sh]][0] + pubkey_hash
lzeroes = (len(s) - len(s.lstrip('0'))) / 2 # non-zero only for ver num '00' (BTC p2pkh)
return ('1' * lzeroes) + _numtob58(int(s+hash256(s)[:8],16))
return ('1' * lzeroes) + _b58chk_encode(s)
# Segwit:
@classmethod
@ -155,7 +184,7 @@ class BitcoinProtocol(MMGenObject):
class BitcoinTestnetProtocol(BitcoinProtocol):
addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
wif_ver_num = 'ef'
wif_ver_num = { 'std': 'ef' }
data_subdir = 'testnet'
daemon_data_subdir = 'testnet3'
rpc_port = 18332
@ -184,7 +213,7 @@ class BitcoinCashProtocol(BitcoinProtocol):
class BitcoinCashTestnetProtocol(BitcoinCashProtocol):
rpc_port = 18442
addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
wif_ver_num = 'ef'
wif_ver_num = { 'std': 'ef' }
data_subdir = 'testnet'
daemon_data_subdir = 'testnet3'
addr_width = 35
@ -202,7 +231,7 @@ class B2XProtocol(BitcoinProtocol):
class B2XTestnetProtocol(B2XProtocol):
addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
wif_ver_num = 'ef'
wif_ver_num = { 'std': 'ef' }
data_subdir = 'testnet'
daemon_data_subdir = 'testnet5'
rpc_port = 18338
@ -215,7 +244,7 @@ class LitecoinProtocol(BitcoinProtocol):
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': ('30','L'), 'p2sh': ('32','M'), 'p2sh2': ('05','3') } # 'p2sh' is new fmt
wif_ver_num = 'b0'
wif_ver_num = { 'std': 'b0' }
secs_per_block = 150
rpc_port = 9332
coin_amt = LTCAmt
@ -226,7 +255,7 @@ class LitecoinProtocol(BitcoinProtocol):
class LitecoinTestnetProtocol(LitecoinProtocol):
# addr ver nums same as Bitcoin testnet, except for 'p2sh'
addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('3a','Q'), 'p2sh2': ('c4','2') }
wif_ver_num = 'ef' # same as Bitcoin testnet
wif_ver_num = { 'std': 'ef' } # same as Bitcoin testnet
data_subdir = 'testnet'
daemon_data_subdir = 'testnet4'
rpc_port = 19332
@ -234,6 +263,8 @@ class LitecoinTestnetProtocol(LitecoinProtocol):
class BitcoinProtocolAddrgen(BitcoinProtocol): mmcaps = ('key','addr')
class BitcoinProtocolKeygen(BitcoinProtocol): mmcaps = ('key',)
class BitcoinTestnetProtocolAddrgen(BitcoinTestnetProtocol): mmcaps = ('key','addr')
class BitcoinTestnetProtocolKeygen(BitcoinTestnetProtocol): mmcaps = ('key',)
class EthereumProtocol(BitcoinProtocolAddrgen):
@ -244,13 +275,13 @@ class EthereumProtocol(BitcoinProtocolAddrgen):
base_coin = 'ETH'
@classmethod
def hex2wif(cls,hexpriv,compressed=False):
def hex2wif(cls,hexpriv,pubkey_type,compressed):
assert compressed == False,'Ethereum does not support compressed pubkeys!'
return str(hexpriv)
@classmethod
def wif2hex(cls,wif):
return { 'hex':str(wif), 'compressed':False }
return { 'hex':str(wif), 'pubkey_type':'std', 'compressed':False }
@classmethod
def verify_addr(cls,addr,return_dict=False):
@ -268,25 +299,34 @@ class EthereumClassicTestnetProtocol(EthereumClassicProtocol): pass
class ZcashProtocol(BitcoinProtocolAddrgen):
name = 'zcash'
base_coin = 'ZEC'
addr_ver_num = { 'p2pkh': ('1cb8','t1'), 'p2sh': ('1cbd','t3') }
wif_ver_num = '80'
mmtypes = ('C',)
addr_ver_num = { 'p2pkh': ('1cb8','t1'), 'p2sh': ('1cbd','t3'), 'zcash_z': ('169a','zc') }
wif_ver_num = { 'std': '80', 'zcash_z': 'ab36' }
mmtypes = ('C','Z')
dfl_mmtype = 'C'
class ZcashTestnetProtocol(object): pass
@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) + hexpriv[2:]
else:
return hexpriv
class ZcashTestnetProtocol(ZcashProtocol):
wif_ver_num = { 'std': '??', 'zcash_z': 'ac08' }
addr_ver_num = { 'p2pkh': ('??','t1'), 'p2sh': ('??','t3'), 'zcash_z': ('16b6','??') }
class DashProtocol(BitcoinProtocolAddrgen):
name = 'dash'
base_coin = 'DASH'
addr_ver_num = { 'p2pkh': ('4c','X'), 'p2sh': ('10','7') }
wif_ver_num = 'cc'
wif_ver_num = { 'std': 'cc' }
mmtypes = ('C',)
dfl_mmtype = 'C'
class DashTestnetProtocol(DashProtocol):
# "Dash", "testnet", "tDASH", b'\xef', b'\x8c', b'\x13'
addr_ver_num = { 'p2pkh': ('8c','y'), 'p2sh': ('13','?') }
wif_ver_num = 'ef'
wif_ver_num = { 'std': 'ef' }
class CoinProtocol(MMGenObject):
coins = {

144
mmgen/sha256.py Executable file
View file

@ -0,0 +1,144 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2017 Philemon <mmgen-py@yandex.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
sha256.py: custom sha256 implementation for Zcash
"""
# Sha256 code ported from JavaScript, originally here:
# CryptoJS v3.1.2 code.google.com/p/crypto-js
# (c) 2009-2013 by Jeff Mott. All rights reserved.
# code.google.com/p/crypto-js/wiki/License
# via here:
# https://www.npmjs.com/package/sha256
# and here:
# https://github.com/howardwu/zaddr
class Sha256(object):
def initConstants():
import math
def isPrime(n):
for factor in range(2,int(math.sqrt(n))+1):
if not (n % factor): return False
return True
def getFractionalBits(n):
return int((n - int(n)) * 0x100000000)
def toSigned32(n): return ((n & 0xffffffff) ^ 0x80000000) - 0x80000000
k = [0] * 64
n,nPrime = 2,0
while nPrime < 64:
if isPrime(n):
k[nPrime] = getFractionalBits(math.pow(n, 1.0 / 3))
# for testing against signed implementations:
# k[nPrime] = toSigned32(getFractionalBits(math.pow(n, 1.0 / 3)))
nPrime += 1
n += 1
return k
K = initConstants()
# preprocess: True=Sha256(), False=Sha256Compress()
def __init__(self,message,preprocess=True):
self.H = [0x6A09E667,0xBB67AE85,0x3C6EF372,0xA54FF53A,0x510E527F,0x9B05688C,0x1F83D9AB,0x5BE0CD19]
self.M = message
self.W = [0] * 64
(self.bytesToWords,self.preprocessBlock)[preprocess]()
# self.initConstants()
self.compute()
def digest(self):
self.M = self.H
self.wordsToBytes()
return self.M
def hexdigest(self):
return self.digest().encode('hex')
def bytesToWords(self):
assert type(self.M) == str
words = [0] * (len(self.M) / 4 + len(self.M) % 4)
b = 0
for i in range(len(self.M)):
words[b >> 5] |= ord(self.M[i]) << (24 - b % 32)
b += 8
self.M = words
def wordsToBytes(self):
assert type(self.M) == list
self.M = ''.join([chr((self.M[b >> 5] >> (24 - b % 32)) & 0xff) for b in range(0,len(self.M)*32,8)])
def preprocessBlock(self):
def lshift(a,b): return (a << b) & 0xffffffff
assert type(self.M) == str
l = len(self.M) * 8
self.bytesToWords()
last_idx = lshift((l + 64 >> 9),4) + 15
self.M.extend([0] * (last_idx - len(self.M) + 1))
self.M[l >> 5] |= lshift(0x80, (24 - l % 32))
self.M[last_idx] = l
def compute(self):
assert type(self.M) == list
for i in range(0,len(self.M),16):
self.processBlock(i)
def processBlock(self,offset):
def lshift(a,b): return (a << b) & 0xffffffff
def sumr(a,b): return (a + b) & 0xffffffff
a,b,c,d,e,f,g,h = [self.H[i] for i in range(8)] # Working variables
for i in range(64):
if i < 16:
self.W[i] = self.M[offset + i]
else:
gamma0x = self.W[i - 15]
gamma0 = ( (lshift(gamma0x,25) | (gamma0x >> 7)) ^
(lshift(gamma0x,14) | (gamma0x >> 18)) ^
(gamma0x >> 3) )
gamma1x = self.W[i - 2]
gamma1 = ( (lshift(gamma1x,15) | (gamma1x >> 17)) ^
(lshift(gamma1x,13) | (gamma1x >> 19)) ^
(gamma1x >> 10) )
self.W[i] = sumr(sumr(sumr(gamma0,self.W[i - 7]),gamma1),self.W[i - 16])
ch = (e & f) ^ (~e & g)
maj = (a & b) ^ (a & c) ^ (b & c)
sigma0 = (lshift(a,30) | (a >> 2)) ^ (lshift(a,19) | (a >> 13)) ^ (lshift(a,10) | (a >> 22))
sigma1 = (lshift(e,26) | (e >> 6)) ^ (lshift(e,21) | (e >> 11)) ^ (lshift(e,7) | (e >> 25))
t1 = sumr(sumr(sumr(sumr(h,sigma1),ch),self.K[i]),self.W[i])
t2 = sumr(sigma0,maj)
h = g
g = f
f = e
e = sumr(d,t1)
d = c
c = b
b = a
a = sumr(t1,t2)
# Intermediate hash value
for n,v in enumerate([a,b,c,d,e,f,g,h]):
self.H[n] = sumr(self.H[n],v)

View file

@ -55,16 +55,16 @@ cmd_data = OrderedDict([
('Hexlify', ['<string> [str-]']),
('Rand2file', ['<outfile> [str]','<nbytes> [str]','threads [int=4]','silent [bool=False]']),
('Randwif', ['compressed [bool=False]']),
('Randpair', ['compressed [bool=False]','segwit [bool=False]']),
('Hex2wif', ['<private key in hex format> [str-]','compressed [bool=False]']),
('Randwif', ["pubkey_type [str='std']",'compressed [bool=False]']),
('Randpair', ["pubkey_type [str='std']",'compressed [bool=False]','segwit [bool=False]']),
('Hex2wif', ['<private key in hex format> [str-]',"pubkey_type [str='std']",'compressed [bool=False]']),
('Wif2hex', ['<wif> [str-]']),
('Wif2addr', ['<wif> [str-]','segwit [bool=False]']),
('Wif2segwit_pair',['<wif> [str-]']),
('Pubhash2addr', ['<coin address in hex format> [str-]','p2sh [bool=False]']),
('Addr2hexaddr', ['<coin address> [str-]']),
('Privhex2addr', ['<private key in hex format> [str-]','compressed [bool=False]','segwit [bool=False]']),
('Privhex2pubhex',['<private key in hex format> [str-]','compressed [bool=False]']),
('Privhex2addr', ['<private key in hex format> [str-]',"pubkey_type [str='std']",'compressed [bool=False]','segwit [bool=False]']),
('Privhex2pubhex',['<private key in hex format> [str-]',"pubkey_type [str='std']",'compressed [bool=False]']),
('Pubhex2addr', ['<public key in hex format> [str-]','p2sh [bool=False]']), # new
('Pubhex2redeem_script',['<public key in hex format> [str-]']), # new
('Wif2redeem_script', ['<private key in WIF format> [str-]']), # new
@ -85,8 +85,8 @@ cmd_data = OrderedDict([
('Add_label', ['<{} address> [str]'.format(pnm),'<label> [str]']),
('Remove_label', ['<{} address> [str]'.format(pnm)]),
('Addrfile_chksum', ['<{} addr file> [str]'.format(pnm)]),
('Keyaddrfile_chksum', ['<{} addr file> [str]'.format(pnm)]),
('Addrfile_chksum', ['<{} addr file> [str]'.format(pnm),"mmtype [str='']"]),
('Keyaddrfile_chksum', ['<{} addr file> [str]'.format(pnm),"mmtype [str='']"]),
('Passwdfile_chksum', ['<{} password file> [str]'.format(pnm)]),
('Find_incog_data', ['<file or device name> [str]','<Incog ID> [str]','keep_searching [bool=False]']),
@ -226,7 +226,7 @@ def print_convert_results(indata,enc,dec,dtype):
if error:
die(3,"Error! Recoded data doesn't match input!")
kg = KeyGenerator()
kg = KeyGenerator('std')
def Hexdump(infile, cols=8, line_nums=True):
Msg(pretty_hexdump(
@ -249,13 +249,13 @@ def B58randenc():
def Randhex(nbytes='32'):
Msg(binascii.hexlify(get_random(int(nbytes))))
def Randwif(compressed=False):
Msg(PrivKey(get_random(32),compressed).wif)
def Randwif(pubkey_type='std',compressed=False):
Msg(PrivKey(get_random(32),compressed=compressed,pubkey_type=pubkey_type).wif)
def Randpair(compressed=False,segwit=False):
def Randpair(pubkey_type='std',compressed=False,segwit=False):
if segwit: compressed = True
ag = AddrGenerator(('p2pkh','segwit')[bool(segwit)])
privhex = PrivKey(get_random(32),compressed)
privhex = PrivKey(get_random(32),compressed=compressed,pubkey_type=pubkey_type)
addr = ag.to_addr(kg.to_pubhex(privhex))
Vmsg('Key (hex): %s' % privhex)
Vmsg_r('Key (WIF): '); Msg(privhex.wif)
@ -283,18 +283,19 @@ def Pubhash2addr(pubhash,p2sh=False): Msg(g.proto.pubhash2addr(pubhash,p2sh=p2
def Addr2hexaddr(addr): Msg(g.proto.verify_addr(addr,return_dict=True)['hex'])
def Hash160(pubkeyhex): Msg(hash160(pubkeyhex))
def Pubhex2addr(pubkeyhex,p2sh=False): Msg(g.proto.pubhash2addr(hash160(pubkeyhex),p2sh=p2sh))
def Wif2hex(wif): Msg(wif2hex(wif))
def Hex2wif(hexpriv,compressed=False):
Msg(g.proto.hex2wif(hexpriv,compressed))
def Privhex2addr(privhex,compressed=False,segwit=False,output_pubhex=False):
def Wif2hex(wif): Msg(PrivKey(wif=wif))
def Hex2wif(hexpriv,pubkey_type='std',compressed=False):
Msg(g.proto.hex2wif(hexpriv,pubkey_type=pubkey_type,compressed=compressed))
def Privhex2addr(privhex,pubkey_type='std',compressed=False,segwit=False,output_pubhex=False):
if segwit and not compressed:
die(1,'Segwit address can be generated only from a compressed pubkey')
pk = PrivKey(binascii.unhexlify(privhex),compressed=compressed)
pk = PrivKey(binascii.unhexlify(privhex),compressed=compressed,pubkey_type=pubkey_type)
ph = kg.to_pubhex(pk)
ag = AddrGenerator(('p2pkh','segwit')[bool(segwit)])
Msg(ph if output_pubhex else ag.to_addr(ph))
def Privhex2pubhex(privhex,compressed=False): # new
return Privhex2addr(privhex,compressed=compressed,output_pubhex=True)
def Privhex2pubhex(privhex,pubkey_type='std',compressed=False): # new
return Privhex2addr(privhex,pubkey_type=pubkey_type,compressed=compressed,output_pubhex=True)
def Pubhex2redeem_script(pubhex): # new
Msg(g.proto.pubhex2redeem_script(pubhex))
def Wif2redeem_script(wif): # new
@ -304,10 +305,6 @@ def Wif2redeem_script(wif): # new
ag = AddrGenerator('segwit')
Msg(ag.to_segwit_redeem_script(kg.to_pubhex(privhex)))
def wif2hex(wif): # wrapper
ret = PrivKey(wif=wif)
return ret or die(1,'{}: Invalid WIF'.format(wif))
wordlists = 'electrum','tirosh'
dfl_wl_id = 'electrum'
@ -353,13 +350,15 @@ def Id6(infile):
def Str2id6(s): # retain ignoring of space for backwards compat
Msg(make_chksum_6(''.join(s.split())))
def Addrfile_chksum(infile):
def Addrfile_chksum(infile,mmtype=''):
from mmgen.addr import AddrList
AddrList(infile,chksum_only=True)
mmtype = None if not mmtype else MMGenAddrType(mmtype)
AddrList(infile,chksum_only=True,mmtype=mmtype)
def Keyaddrfile_chksum(infile):
def Keyaddrfile_chksum(infile,mmtype=''):
from mmgen.addr import KeyAddrList
KeyAddrList(infile,chksum_only=True)
mmtype = None if not mmtype else MMGenAddrType(mmtype)
KeyAddrList(infile,chksum_only=True,mmtype=mmtype)
def Passwdfile_chksum(infile):
from mmgen.addr import PasswordList

View file

@ -597,7 +597,7 @@ class MMGenTX(MMGenObject):
if self.has_segwit_inputs():
from mmgen.addr import KeyGenerator,AddrGenerator
kg = KeyGenerator()
kg = KeyGenerator('std')
ag = AddrGenerator('segwit')
keydict = MMGenDict([(d.addr,d.sec) for d in keys])

View file

@ -99,15 +99,15 @@ f_obj='Data object test complete'
i_alts='Gen-only altcoin'
s_alts='The following tests will test generation operations for all supported altcoins'
t_alts=(
'test/scrambletest.py'
'test/test.py -n altcoin_ref'
'test/gentest.py --coin=btc 2:ext 100'
'test/gentest.py --coin=ltc 2:ext 100'
'test/gentest.py --coin=zec 2:ext 100'
'test/gentest.py --coin=dash 2:ext 100'
'test/gentest.py --coin=zec 2:ext 100'
'test/gentest.py --coin=etc 2:ext 100'
'test/gentest.py --coin=eth 2:ext 100'
'test/scrambletest.py'
)
'test/gentest.py --coin=zec --type=zcash_z 2:ext 1000')
f_alts='Gen-only altcoin tests completed'
i_misc='Miscellaneous operations' # includes autosign!

View file

@ -123,6 +123,7 @@ setup(
'mmgen.regtest',
'mmgen.rpc',
'mmgen.seed',
'mmgen.sha256',
'mmgen.term',
'mmgen.test',
'mmgen.tool',

View file

@ -38,6 +38,7 @@ opts_data = lambda: {
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-q, --quiet Produce quieter output
-a, --type= Specify address type (options: 'std','zcash_z')
-s, --segwit Generate Segwit (P2SH-P2WPKH) addresses
-v, --verbose Produce more verbose output
""",
@ -72,9 +73,16 @@ cmd_args = opts.init(opts_data,add_opts=['exact_output'])
if not 1 <= len(cmd_args) <= 2: opts.usage()
addr_type = opt.type or 'std'
def pyethereum_sec2addr(sec):
return sec,eth.privtoaddr(sec).encode('hex')
def zcash_mini_sec2addr(sec):
p = sp.Popen(['zcash-mini','-key','-simple'],stderr=sp.PIPE,stdin=sp.PIPE,stdout=sp.PIPE)
p.stdin.write(sec.wif+'\n')
return sec.wif,p.stdout.read().split()[0]
def pycoin_sec2addr(sec):
if g.testnet: # pycoin/networks/all.py pycoin/networks/legacy_networks.py
coin = { 'BTC':'XTN', 'LTC':'XLT', 'DASH':'tDASH' }[g.coin]
@ -123,12 +131,16 @@ else:
a = int(a)
assert 1 <= a <= len(g.key_generators)
if b == 'ext':
if g.coin in ('ETH','ETC'):
if addr_type == 'zcash_z':
import subprocess as sp
ext_sec2addr = zcash_mini_sec2addr
ext_lib = 'zcash_mini'
elif g.coin in ('ETH','ETC'):
try:
import ethereum.utils as eth
except:
die(1,"Unable to import 'pyethereum' module. Is pyethereum installed?")
sec2addr = pyethereum_sec2addr
ext_sec2addr = pyethereum_sec2addr
ext_lib = 'pyethereum'
else:
try:
@ -136,7 +148,7 @@ else:
except:
die(1,"Unable to import module 'ku'. Is pycoin installed?")
PREFIX_TRANSFORMS = pcku.prefix_transforms_for_network(g.coin)
sec2addr = pycoin_sec2addr
ext_sec2addr = pycoin_sec2addr
ext_lib = 'pycoin'
else:
b = int(b)
@ -156,27 +168,33 @@ def match_error(sec,wif,a_addr,b_addr,a,b):
""".format(sec,wif,a_addr,b_addr,pnm=g.proj_name,a=m[a],b=m[b] if b in m else b).rstrip())
# Begin execution
compressed = False if g.coin in ('ETH','ETC') else True
no_compressed = g.coin in ('ETH','ETC') or addr_type == 'zcash_z'
no_uncompressed = opt.segwit or g.coin == 'DASH' or (g.coin=='ZEC' and addr_type == 'std')
switch_compressed = not no_compressed and not no_uncompressed
compressed = not no_compressed
from mmgen.addr import KeyGenerator,AddrGenerator
from mmgen.obj import PrivKey
ag = AddrGenerator('ethereum' if g.coin in ('ETH','ETC') else ('p2pkh','segwit')[bool(opt.segwit)])
ag = AddrGenerator(
'ethereum' if g.coin in ('ETH','ETC')
else 'zcash_z' if addr_type == 'zcash_z'
else ('p2pkh','segwit')[bool(opt.segwit)])
if a and b:
m = "Comparing address generators '{}' and '{}' for coin {}"
qmsg(green(m.format(g.key_generators[a-1],(ext_lib if b == 'ext' else g.key_generators[b-1]),g.coin)))
last_t = time.time()
kg_a = KeyGenerator(a)
if b != 'ext': kg_b = KeyGenerator(b)
kg_a = KeyGenerator(addr_type,a)
if b != 'ext': kg_b = KeyGenerator(addr_type,b)
qmsg(green(m.format(kg_a.desc,(ext_lib if b == 'ext' else kg_b.desc),g.coin)))
for i in range(rounds):
if opt.verbose or time.time() - last_t >= 0.1:
qmsg_r('\rRound %s/%s ' % (i+1,rounds))
last_t = time.time()
sec = PrivKey(os.urandom(32),compressed)
sec = PrivKey(os.urandom(32),compressed=compressed,pubkey_type=addr_type)
a_addr = ag.to_addr(kg_a.to_pubhex(sec))
if b == 'ext':
b_wif,b_addr = sec2addr(sec)
b_wif,b_addr = ext_sec2addr(sec)
if b_wif != sec.wif:
match_error(sec,sec.wif,sec.wif,b_wif,a,b)
else:
@ -184,38 +202,38 @@ if a and b:
vmsg('\nkey: %s\naddr: %s\n' % (sec.wif,a_addr))
if a_addr != b_addr:
match_error(sec,sec.wif,a_addr,b_addr,a,ext_lib if b == 'ext' else b)
if not opt.segwit and 'L' in g.proto.mmtypes:
if switch_compressed:
compressed = not compressed
qmsg_r('\rRound %s/%s ' % (i+1,rounds))
qmsg(green(('\n','')[bool(opt.verbose)] + 'OK'))
elif a and not fh:
kg = KeyGenerator(addr_type,a)
m = "Testing speed of address generator '{}' for coin {}"
qmsg(green(m.format(g.key_generators[a-1],g.coin)))
qmsg(green(m.format(kg.desc,g.coin)))
from struct import pack,unpack
seed = os.urandom(28)
print 'Incrementing key with each round'
print 'Starting key:', hexlify(seed+pack('I',0))
import time
start = last_t = time.time()
kg = KeyGenerator(a)
for i in range(rounds):
if time.time() - last_t >= 0.1:
qmsg_r('\rRound %s/%s ' % (i+1,rounds))
last_t = time.time()
sec = PrivKey(seed+pack('I',i),compressed)
sec = PrivKey(seed+pack('I',i),compressed=compressed,pubkey_type=addr_type)
a_addr = ag.to_addr(kg.to_pubhex(sec))
vmsg('\nkey: %s\naddr: %s\n' % (sec.wif,a_addr))
if not opt.segwit and g.coin not in ('ETC','ETC'):
if switch_compressed:
compressed = not compressed
qmsg_r('\rRound %s/%s ' % (i+1,rounds))
qmsg('\n{} addresses generated in {:.2f} seconds'.format(rounds,time.time()-start))
elif a and dump:
kg = KeyGenerator(addr_type,a)
m = "Comparing output of address generator '{}' against wallet dump '{}'"
qmsg(green(m.format(g.key_generators[a-1],cmd_args[1])))
kg = KeyGenerator(a)
qmsg(green(m.format(kg.desc,cmd_args[1])))
for n,[wif,a_addr] in enumerate(dump,1):
qmsg_r('\rKey %s/%s ' % (n,len(dump)))
try:

View file

@ -0,0 +1,19 @@
# MMGen address file
#
# This file is editable.
# Everything following a hash symbol '#' is a comment and ignored by MMGen.
# A text label of 32 characters or less may be added to the right of each
# address, and it will be appended to the tracking wallet label upon import.
# The label may contain any printable ASCII symbol.
# Address data checksum for 98831F3A-ZEC-Z[1,31-33,500-501,1010-1011]: 9C7A 72DC 3D4A B3AF
# Record this value to a secure location.
98831F3A ZEC:ZCASH_Z {
1 zcLMMsnzfFYZWU4avRBnuc83yh4jTtJXbtP32uWrs3ickzu1krMU4ppZCQPTwwfE9hLnRuFDSYF8VFW13aT9eeQK8aov3Ge
31 zcYNDA2gcYTU3fUGNRoyBS7KDN3gqGY3STRGeJViwkwmqFqAFPuw3UzXnbmtNPVvp5EQn4H6opjF8nd3touN2J9QK6HrDVW
32 zcG4tWh4WmrD2RUhXKE3PU8R7oPnPaSgsKmf2gCCGbQPL5RG1tjcZojHwhVjCxbsUqGgV4hy6gfYsYAgsvFCsLTKpqbENuP
33 zcfWPcKVvKAVRuY3rG8m7RgrCSB7pWYiXfQpr3ppWaHwQ6rWGrXp8YfeZMq53ZYZ4E7RASm8DvcnCW3bVWTsC5vgEkRpmYS
500 zcdnkn6chKxCVuZ1wstUdGUHx4hYNsDeErqwc8zXuZnGekTVqYLTWzNWT5LgJSZYaVnJ8wiL1mWJuMV6ACh3yaC68p39nVT
501 zcQctEy3rwKuKSXPFBzanTVJGGbWrSCfymQt18rcRC41pdb8g2A2GbRdxkdBRs9WysDwGWySweYZzwQWL2LvoqrGeuTaZAh
1010 zcFeZC94UYSRfUg4MsxsPzmczj93WCuSRAqBsFwCQ3sqpg1LE1eksyRRSis511fBsse1GpJdqW35VAcAtR2rswxEAtwW6jt
1011 zcanQ9hr8FpxGyBG1HYYBLCkftFf9tyqGpzYUe6eVM5fHuZJUMv22Vpb29B8iHjYxXq1WQHHGNmZrzHW5btwAMrUAom584A
}

View file

@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
test/scrambletest.py: seed scrambling and addrlist metadata generation tests for all supported altcoins
test/scrambletest.py: seed scrambling and addrlist data generation tests for all supported altcoins
"""
import sys,os,subprocess
@ -29,7 +29,7 @@ sys.path.__setitem__(0,repo_root)
from mmgen.common import *
opts_data = lambda: {
'desc': 'Test seed scrambling and addrlist metadata generation for all supported altcoins',
'desc': 'Test seed scrambling and addrlist data generation for all supported altcoins',
'usage':'[options] [command]',
'options': """
-h, --help Print this help message
@ -53,24 +53,25 @@ if not opt.system:
from collections import OrderedDict
test_data = OrderedDict([
# SCRAMBLED_SEED[:8] SCRAMBLE_KEY ID_STR LBL
('btc', ('456d7f5f1c4bfe3b', '(none)', '', '')),
('btc_compressed', ('bf98a4af5464a4ef', 'compressed', '-C', 'COMPRESSED')),
('btc_segwit', ('b56962d829ffc678', 'segwit', '-S', 'SEGWIT')),
('bch', ('456d7f5f1c4bfe3b', '(none)', '', '')),
('bch_compressed', ('bf98a4af5464a4ef', 'compressed', '-C', 'COMPRESSED')),
('ltc', ('b11f16632e63ba92', 'ltc:legacy', '-LTC', 'LTC')),
('ltc_compressed', ('7ccf465d466ee7d3', 'ltc:compressed', '-LTC-C', 'LTC:COMPRESSED')),
('ltc_segwit', ('9460f5ba15e82768', 'ltc:segwit', '-LTC-S', 'LTC:SEGWIT')),
('dash', ('bb21cf88c198ab8c', 'dash:compressed','-DASH-C', 'DASH:COMPRESSED')),
('zec', ('637f7b8117b524ed', 'zec:compressed', '-ZEC-C', 'ZEC:COMPRESSED')),
('eth', ('213ed116869b19f2', 'eth', '-ETH', 'ETH')),
('etc', ('909def37096f5ab8', 'etc', '-ETC', 'ETC')),
# SCRAMBLED_SEED[:8] SCRAMBLE_KEY ID_STR LBL FIRST ADDR
('btc', ('456d7f5f1c4bfe3b','(none)', '', '', '1MU7EdgqYy9JX35L25hR6CmXXcSEBDAwyv')),
('btc_compressed',('bf98a4af5464a4ef','compressed', '-C', 'COMPRESSED','1F97Jd89wwmu4ELadesAdGDzg3d8Y6j5iP')),
('btc_segwit', ('b56962d829ffc678','segwit', '-S', 'SEGWIT', '36TvVzU5mxSjJ3D9qKAmYzCV7iUqtTDezF')),
('bch', ('456d7f5f1c4bfe3b','(none)', '', '', '1MU7EdgqYy9JX35L25hR6CmXXcSEBDAwyv')),
('bch_compressed',('bf98a4af5464a4ef','compressed', '-C', 'COMPRESSED','1F97Jd89wwmu4ELadesAdGDzg3d8Y6j5iP')),
('ltc', ('b11f16632e63ba92','ltc:legacy', '-LTC','LTC', 'LMxB474SVfxeYdqxNrM1WZDZMnifteSMv1')),
('ltc_compressed',('7ccf465d466ee7d3','ltc:compressed', '-LTC-C', 'LTC:COMPRESSED', 'LdkebBKVXSs6NNoPJWGM8KciDnL8LhXXjb')),
('ltc_segwit', ('9460f5ba15e82768','ltc:segwit', '-LTC-S', 'LTC:SEGWIT', 'MQrY3vEbqKMBgegXrSaR93R2HoTDE5bKrY')),
('eth', ('213ed116869b19f2','eth', '-ETH', 'ETH','e704b6cfd9f0edb2e6cfbd0c913438d37ede7b35')),
('etc', ('909def37096f5ab8','etc', '-ETC', 'ETC','1a6acbef8c38f52f20d04ecded2992b04d8608d7')),
('dash', ('bb21cf88c198ab8c','dash:compressed','-DASH-C','DASH:COMPRESSED','XsjAJvCxkxYh55ZvCZMFEv2eJUVo5xxbwi')),
('zec', ('637f7b8117b524ed','zec:compressed', '-ZEC-C', 'ZEC:COMPRESSED', 't1d47QeTehQye4Mms1Lmx7dPjKVoTtHXKmu')),
('zec_zcash_z', ('b15570d033df9b1a','zec:zcash_z', '-ZEC-Z', 'ZEC:ZCASH_Z', 'zcLMMsnzfFYZWU4avRBnuc83yh4jTtJXbtP32uWrs3ickzu1krMU4ppZCQPTwwfE9hLnRuFDSYF8VFW13aT9eeQK8aov3Ge')),
])
def run_tests():
for test in test_data:
try: coin,mmtype = test.split('_')
try: coin,mmtype = test.split('_',1)
except: coin,mmtype = test,None
cmd_name = 'cmds/mmgen-addrgen'
wf = 'test/ref/98831F3A.mmwords'
@ -79,15 +80,15 @@ def run_tests():
vmsg(green('Executing: {}'.format(' '.join(cmd))))
msg_r('Testing: --coin {:4} {:22}'.format(coin.upper(),type_arg[0] if type_arg else ''))
p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
o = p.stdout.read().splitlines()
# pmsg(o)
o = p.stdout.read()
vmsg(o)
o = o.splitlines()
d = [e for e in o if len(e) > 4 and e[:9] == 'sc_debug_']
# pmsg(d)
for n,k in enumerate(['seed','str','id_str','lbl']):
d.append('sc_debug_addr: ' + o[-2].split()[-1])
for n,k in enumerate(['seed','str','id_str','lbl','addr']):
kk = 'sc_debug_'+k
a = test_data[test][n]
b = [e for e in d if e[:len(kk)] == kk][0][len(kk)+2:]
# pmsg(b); continue
if b == a:
vmsg('sc_{}: {}'.format(k,a))
else:

View file

@ -448,10 +448,12 @@ cfgs = {
'ltc': ('B804 978A 8796 3ED4','93A6 844C 8ECC BEF4'),
},
'ref_addrfile_chksum_zec': '903E 7225 DD86 6E01',
'ref_addrfile_chksum_zec_z': '9C7A 72DC 3D4A B3AF',
'ref_addrfile_chksum_dash':'FBC1 6B6A 0988 4403',
'ref_addrfile_chksum_eth': 'E554 076E 7AF6 66A3',
'ref_addrfile_chksum_etc': 'E97A D796 B495 E8BC',
'ref_keyaddrfile_chksum_zec': 'F05A 5A5C 0C8E 2617',
'ref_keyaddrfile_chksum_zec_z': '220F 5F23 CC9B EC1F',
'ref_keyaddrfile_chksum_dash': 'E83D 2C63 FEA2 4142',
'ref_keyaddrfile_chksum_eth': '3635 4DCF B752 8772',
'ref_keyaddrfile_chksum_etc': '9BAC 38E7 5C8E 42E0',
@ -709,14 +711,16 @@ cmd_group['misc'] = (
)
cmd_group['altcoin_ref'] = (
('ref_addrfile_chk_zec', 'reference address file (ZEC)'),
('ref_addrfile_chk_dash','reference address file (DASH)'),
('ref_addrfile_chk_eth', 'reference address file (ETH)'),
('ref_addrfile_chk_etc', 'reference address file (ETC)'),
('ref_keyaddrfile_chk_zec', 'reference key-address file (ZEC)'),
('ref_keyaddrfile_chk_dash','reference key-address file (DASH)'),
('ref_addrfile_chk_dash','reference address file (DASH)'),
('ref_addrfile_chk_zec', 'reference address file (ZEC-T)'),
('ref_addrfile_chk_zec_z','reference address file (ZEC-Z)'),
('ref_keyaddrfile_chk_eth', 'reference key-address file (ETH)'),
('ref_keyaddrfile_chk_etc', 'reference key-address file (ETC)'),
('ref_keyaddrfile_chk_dash','reference key-address file (DASH)'),
('ref_keyaddrfile_chk_zec', 'reference key-address file (ZEC-T)'),
('ref_keyaddrfile_chk_zec_z','reference key-address file (ZEC-Z)'),
)
# undocumented admin cmds
@ -1041,8 +1045,8 @@ def create_fake_unspent_data(adata,tx_data,non_mmgen_input=''):
out.append(create_fake_unspent_entry(coinaddr,d['al_id'],idx,lbl,segwit=d['segwit']))
if non_mmgen_input:
privkey = PrivKey(os.urandom(32),compressed=True)
coinaddr = AddrGenerator('p2pkh').to_addr(KeyGenerator().to_pubhex(privkey))
privkey = PrivKey(os.urandom(32),compressed=True,pubkey_type='std')
coinaddr = AddrGenerator('p2pkh').to_addr(KeyGenerator('std').to_pubhex(privkey))
of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
write_data_to_file(of,privkey.wif+'\n','compressed {} key'.format(g.proto.name),silent=True)
out.append(create_fake_unspent_entry(coinaddr,non_mmgen=True,segwit=False))
@ -1083,7 +1087,7 @@ def create_tx_data(sources):
def make_txcreate_cmdline(tx_data):
privkey = PrivKey(os.urandom(32),compressed=True)
t = ('p2pkh','segwit')['S' in g.proto.mmtypes]
coinaddr = AddrGenerator(t).to_addr(KeyGenerator().to_pubhex(privkey))
coinaddr = AddrGenerator(t).to_addr(KeyGenerator('std').to_pubhex(privkey))
# total of two outputs must be < 10 BTC (<1000 LTC)
mods = {'btc':(6,4),'bch':(6,4),'ltc':(600,400)}[coin_sel]
@ -2030,12 +2034,12 @@ class MMGenTestSuite(object):
t.close()
cmp_or_die(cfg['seed_id'],chk)
def ref_addrfile_chk(self,name,ftype='addr',coin=None,subdir=None,pfx=None,mmtype=None):
def ref_addrfile_chk(self,name,ftype='addr',coin=None,subdir=None,pfx=None,mmtype=None,add_args=[]):
af_key = 'ref_{}file'.format(ftype)
af_fn = cfg[af_key].format(pfx or altcoin_pfx,'' if coin else tn_ext)
af = os.path.join(ref_dir,(subdir or ref_subdir,'')[ftype=='passwd'],af_fn)
coin_arg = [] if coin == None else ['--coin='+coin]
t = MMGenExpect(name,'mmgen-tool',coin_arg+[ftype.replace('segwit','')+'file_chksum',af])
t = MMGenExpect(name,'mmgen-tool',coin_arg+[ftype.replace('segwit','')+'file_chksum',af]+add_args)
if ftype == 'keyaddr':
w = 'key-address data'
t.hash_preset(w,ref_kafile_hash_preset)
@ -2051,6 +2055,10 @@ class MMGenTestSuite(object):
def ref_addrfile_chk_zec(self,name):
self.ref_addrfile_chk(name,ftype='addr',coin='ZEC',subdir='zcash',pfx='-ZEC-C')
def ref_addrfile_chk_zec_z(self,name):
self.ref_addrfile_chk(name,ftype='addr',coin='ZEC',subdir='zcash',pfx='-ZEC-Z',
mmtype='z',add_args=['mmtype=zcash_z'])
def ref_addrfile_chk_dash(self,name):
self.ref_addrfile_chk(name,ftype='addr',coin='DASH',subdir='dash',pfx='-DASH-C')
@ -2063,6 +2071,10 @@ class MMGenTestSuite(object):
def ref_keyaddrfile_chk_zec(self,name):
self.ref_addrfile_chk(name,ftype='keyaddr',coin='ZEC',subdir='zcash',pfx='-ZEC-C')
def ref_keyaddrfile_chk_zec_z(self,name):
self.ref_addrfile_chk(name,ftype='keyaddr',coin='ZEC',subdir='zcash',pfx='-ZEC-Z',
mmtype='z',add_args=['mmtype=zcash_z'])
def ref_keyaddrfile_chk_dash(self,name):
self.ref_addrfile_chk(name,ftype='keyaddr',coin='DASH',subdir='dash',pfx='-DASH-C')
@ -2604,7 +2616,7 @@ class MMGenTestSuite(object):
psave = g.proto
g.proto = CoinProtocol(g.coin,True)
privhex = PrivKey(os.urandom(32),compressed=True)
addr = AddrGenerator('p2pkh').to_addr(KeyGenerator().to_pubhex(privhex))
addr = AddrGenerator('p2pkh').to_addr(KeyGenerator('std').to_pubhex(privhex))
g.proto = psave
outputs_cl = [sid+':{}:3,1.1234'.format(g.proto.mmtypes[-1]), sid+':C:5,5.5555',sid+':L:4',addr+',100']
pw = cfg['wpasswd']