2018-10-30 16:23:12 +00:00
|
|
|
#!/usr/bin/env python3
|
2016-02-28 16:41:43 +03:00
|
|
|
#
|
|
|
|
|
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
2020-02-18 14:07:27 +00:00
|
|
|
# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
|
2016-02-28 16:41:43 +03:00
|
|
|
#
|
|
|
|
|
# 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/>.
|
|
|
|
|
|
|
|
|
|
"""
|
2017-10-28 00:11:00 +03:00
|
|
|
test/gentest.py: Cryptocoin key/address generation tests for the MMGen suite
|
2016-02-28 16:41:43 +03:00
|
|
|
"""
|
2015-01-12 00:07:21 +03:00
|
|
|
|
|
|
|
|
import sys,os
|
|
|
|
|
pn = os.path.dirname(sys.argv[0])
|
|
|
|
|
os.chdir(os.path.join(pn,os.pardir))
|
|
|
|
|
sys.path.__setitem__(0,os.path.abspath(os.curdir))
|
Key/address generation support for 144 altcoins
Support for these coins is EXPERIMENTAL, use at your own risk
EXAMPLE: generate 10 Dogecoin key/address pairs with your default wallet:
`mmgen-keygen --coin=doge 1-10`
Keys for different coins are distinct, so users needn't worry about key reuse.
Supported alts: 2give,42,611,ac,acoin,alf,anc,apex,arco,arg,aur,b2x,bcf,bch,blk,bmc,bqc,bsty,btcd,btq,bucks,cann,cash,cat,cbx,ccn,cdn,chc,clam,con,cpc,crps,csh,dash,dcr,dfc,dgb,dgc,doge,doged,dope,dvc,efl,emc,emd,enrg,esp,etc,eth,fai,fc2,fibre,fjc,flo,flt,fst,ftc,gcr,good,grc,gun,ham,html5,hyp,icash,infx,inpay,ipc,jbs,judge,lana,lat,ldoge,lmc,ltc,mars,mcar,mec,mint,mobi,mona,moon,mrs,mue,mxt,myr,myriad,mzc,neos,neva,nka,nlg,nmc,nto,nvc,ok,omc,omni,onion,onx,part,pink,pivx,pkb,pnd,pot,ppc,ptc,pxc,qrk,rain,rbt,rby,rdd,ric,sdc,sib,smly,song,spr,start,sys,taj,tit,tpc,trc,ttc,tx,uno,via,vpn,vtc,wash,wdc,wisc,wkc,wsx,xcn,xgb,xmg,xpm,xpoke,xred,xst,xvc,zec,zet,zlq,zoom,zrc
Test the new functionality with `scripts/test-release.sh -Pn master alts`
B2X support disabled pending further testing
2017-12-24 15:26:23 +03:00
|
|
|
os.environ['MMGEN_TEST_SUITE'] = '1'
|
2015-01-12 00:07:21 +03:00
|
|
|
|
2016-02-28 16:41:43 +03:00
|
|
|
# Import these _after_ local path's been added to sys.path
|
|
|
|
|
from mmgen.common import *
|
2015-01-12 00:07:21 +03:00
|
|
|
|
|
|
|
|
rounds = 100
|
2019-03-26 12:59:30 +00:00
|
|
|
opts_data = {
|
|
|
|
|
'text': {
|
2019-11-06 17:34:26 +00:00
|
|
|
'desc': 'Test key/address generation of the MMGen suite in various ways',
|
2019-03-26 12:59:30 +00:00
|
|
|
'usage':'[options] [spec] [rounds | dump file]',
|
|
|
|
|
'options': """
|
2016-12-08 18:02:28 +03:00
|
|
|
-h, --help Print this help message
|
2019-11-04 16:48:25 +00:00
|
|
|
-a, --all Test all coins supported by specified external tool
|
2019-03-23 14:21:34 +00:00
|
|
|
-k, --use-internal-keccak-module Force use of the internal keccak module
|
2016-12-08 18:02:28 +03:00
|
|
|
--, --longhelp Print help message for long options (common options)
|
|
|
|
|
-q, --quiet Produce quieter output
|
2019-11-04 16:48:25 +00:00
|
|
|
-t, --type=t Specify address type (e.g. 'compressed','segwit','zcash_z','bech32')
|
2016-12-08 18:02:28 +03:00
|
|
|
-v, --verbose Produce more verbose output
|
2015-01-12 00:07:21 +03:00
|
|
|
""",
|
|
|
|
|
'notes': """
|
2019-11-04 16:48:25 +00:00
|
|
|
TEST TYPES:
|
|
|
|
|
|
|
|
|
|
A/B: {prog} A:B [rounds] (compare key generators A and B)
|
|
|
|
|
Speed: {prog} A [rounds] (test speed of key generator A)
|
|
|
|
|
Compare: {prog} A <dump file> (compare generator A to wallet dump)
|
|
|
|
|
|
|
|
|
|
where A and B are one of:
|
|
|
|
|
'1' - native Python ECDSA library (slow), or
|
|
|
|
|
'2' - bitcoincore.org's libsecp256k1 library (default);
|
|
|
|
|
or:
|
|
|
|
|
B is name of an external tool (see below) or 'ext'.
|
|
|
|
|
If B is 'ext', the external tool will be chosen automatically.
|
2015-01-12 00:07:21 +03:00
|
|
|
|
2016-11-11 16:05:27 +03:00
|
|
|
EXAMPLES:
|
2019-11-04 16:48:25 +00:00
|
|
|
|
|
|
|
|
Compare addresses generated by native Python ECDSA library and libsecp256k1,
|
|
|
|
|
100 rounds:
|
|
|
|
|
$ {prog} 1:2 100
|
|
|
|
|
|
|
|
|
|
Compare mmgen-secp256k1 Segwit address generation to pycoin library for all
|
|
|
|
|
supported coins, 100 rounds:
|
|
|
|
|
$ {prog} --all --type=segwit 2:pycoin 100
|
|
|
|
|
|
|
|
|
|
Compare mmgen-secp256k1 address generation to keyconv tool for all
|
|
|
|
|
supported coins, 100 rounds:
|
|
|
|
|
$ {prog} --all --type=compressed 2:keyconv 100
|
|
|
|
|
|
|
|
|
|
Compare mmgen-secp256k1 XMR address generation to configured external tool,
|
|
|
|
|
10 rounds:
|
|
|
|
|
$ {prog} --coin=xmr 2:ext 10
|
|
|
|
|
|
|
|
|
|
Test speed of mmgen-secp256k1 address generation, 10,000 rounds:
|
|
|
|
|
$ {prog} 2 10000
|
|
|
|
|
|
2020-06-13 10:21:01 +00:00
|
|
|
Compare mmgen-secp256k1-generated bech32 addrs to coin daemon wallet dump:
|
2019-11-04 16:48:25 +00:00
|
|
|
$ {prog} --type=bech32 2 bech32wallet.dump
|
|
|
|
|
|
|
|
|
|
Supported external tools:
|
|
|
|
|
|
|
|
|
|
+ ethkey (for ETH,ETC)
|
|
|
|
|
https://github.com/paritytech/parity-ethereum
|
|
|
|
|
(build with 'cargo build -p ethkey-cli --release')
|
|
|
|
|
|
|
|
|
|
+ zcash-mini (for Zcash Z-addresses)
|
|
|
|
|
https://github.com/FiloSottile/zcash-mini
|
|
|
|
|
|
|
|
|
|
+ moneropy (for Monero addresses)
|
|
|
|
|
https://github.com/bigreddmachine/MoneroPy
|
|
|
|
|
|
|
|
|
|
+ pycoin (for supported coins)
|
|
|
|
|
https://github.com/richardkiss/pycoin
|
|
|
|
|
|
|
|
|
|
+ keyconv (for supported coins)
|
|
|
|
|
https://github.com/exploitagency/vanitygen-plus
|
|
|
|
|
('keyconv' does not generate Segwit addresses)
|
2019-03-26 12:59:30 +00:00
|
|
|
"""
|
|
|
|
|
},
|
|
|
|
|
'code': {
|
|
|
|
|
'notes': lambda s: s.format(
|
2019-11-04 16:48:25 +00:00
|
|
|
prog='test/gentest.py',
|
2019-03-26 12:59:30 +00:00
|
|
|
pnm=g.proj_name,
|
2020-06-13 10:21:01 +00:00
|
|
|
snum=rounds )
|
2019-03-26 12:59:30 +00:00
|
|
|
}
|
2015-01-12 00:07:21 +03:00
|
|
|
}
|
2016-11-21 19:59:03 +03:00
|
|
|
|
|
|
|
|
sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
|
|
|
|
|
|
2019-03-22 11:32:22 +00:00
|
|
|
cmd_args = opts.init(opts_data,add_opts=['exact_output','use_old_ed25519'])
|
2015-01-12 00:07:21 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
if not 1 <= len(cmd_args) <= 2:
|
|
|
|
|
opts.usage()
|
2019-11-02 09:34:21 +00:00
|
|
|
|
2020-05-28 09:53:34 +00:00
|
|
|
from mmgen.protocol import init_proto_from_opts
|
|
|
|
|
proto = init_proto_from_opts()
|
|
|
|
|
|
2019-10-23 09:31:21 +00:00
|
|
|
from subprocess import run,PIPE,DEVNULL
|
|
|
|
|
def get_cmd_output(cmd,input=None):
|
|
|
|
|
return run(cmd,input=input,stdout=PIPE,stderr=DEVNULL).stdout.decode().splitlines()
|
|
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
from collections import namedtuple
|
|
|
|
|
gtr = namedtuple('gen_tool_result',['wif','addr','vk'])
|
|
|
|
|
|
2019-11-06 17:35:14 +00:00
|
|
|
class GenTool(object):
|
|
|
|
|
|
|
|
|
|
def run_tool(self,sec):
|
2020-05-28 09:53:34 +00:00
|
|
|
vcoin = 'BTC' if proto.coin == 'BCH' else proto.coin
|
2019-11-06 17:35:14 +00:00
|
|
|
return self.run(sec,vcoin)
|
2019-11-04 16:48:25 +00:00
|
|
|
|
|
|
|
|
class GenToolEthkey(GenTool):
|
|
|
|
|
desc = 'ethkey'
|
|
|
|
|
def __init__(self):
|
2020-05-28 09:53:34 +00:00
|
|
|
proto = init_proto('eth')
|
2019-11-04 16:48:25 +00:00
|
|
|
global addr_type
|
2020-05-28 09:53:34 +00:00
|
|
|
addr_type = MMGenAddrType(proto,'E')
|
2019-11-04 16:48:25 +00:00
|
|
|
|
2019-11-06 17:35:14 +00:00
|
|
|
def run(self,sec,vcoin):
|
2019-11-04 16:48:25 +00:00
|
|
|
o = get_cmd_output(['ethkey','info',sec])
|
|
|
|
|
return gtr(o[0].split()[1],o[-1].split()[1],None)
|
|
|
|
|
|
|
|
|
|
class GenToolKeyconv(GenTool):
|
|
|
|
|
desc = 'keyconv'
|
2019-11-06 17:35:14 +00:00
|
|
|
def run(self,sec,vcoin):
|
|
|
|
|
o = get_cmd_output(['keyconv','-C',vcoin,sec.wif])
|
2019-11-04 16:48:25 +00:00
|
|
|
return gtr(o[1].split()[1],o[0].split()[1],None)
|
|
|
|
|
|
|
|
|
|
class GenToolZcash_mini(GenTool):
|
|
|
|
|
desc = 'zcash-mini'
|
|
|
|
|
def __init__(self):
|
2020-05-28 09:53:34 +00:00
|
|
|
proto = init_proto('zec')
|
2019-11-04 16:48:25 +00:00
|
|
|
global addr_type
|
2020-05-28 09:53:34 +00:00
|
|
|
addr_type = MMGenAddrType(proto,'Z')
|
2019-11-04 16:48:25 +00:00
|
|
|
|
2019-11-06 17:35:14 +00:00
|
|
|
def run(self,sec,vcoin):
|
2019-11-04 16:48:25 +00:00
|
|
|
o = get_cmd_output(['zcash-mini','-key','-simple'],input=(sec.wif+'\n').encode())
|
|
|
|
|
return gtr(o[1],o[0],o[-1])
|
|
|
|
|
|
|
|
|
|
class GenToolPycoin(GenTool):
|
|
|
|
|
"""
|
|
|
|
|
pycoin/networks/all.py pycoin/networks/legacy_networks.py
|
|
|
|
|
"""
|
|
|
|
|
desc = 'pycoin'
|
|
|
|
|
def __init__(self):
|
|
|
|
|
m = "Unable to import pycoin.networks.registry. Is pycoin installed on your system?"
|
2017-12-18 20:19:01 +03:00
|
|
|
try:
|
2018-05-09 14:28:00 +00:00
|
|
|
from pycoin.networks.registry import network_for_netcode
|
2017-12-18 20:19:01 +03:00
|
|
|
except:
|
2019-11-04 16:48:25 +00:00
|
|
|
raise ImportError(m)
|
|
|
|
|
self.nfnc = network_for_netcode
|
|
|
|
|
|
2019-11-06 17:35:14 +00:00
|
|
|
def run(self,sec,vcoin):
|
2020-05-28 09:53:34 +00:00
|
|
|
if proto.testnet:
|
2019-11-06 17:35:14 +00:00
|
|
|
vcoin = ci.external_tests['testnet']['pycoin'][vcoin]
|
|
|
|
|
network = self.nfnc(vcoin)
|
2019-11-04 16:48:25 +00:00
|
|
|
key = network.keys.private(secret_exponent=int(sec,16),is_compressed=addr_type.name != 'legacy')
|
|
|
|
|
if key is None:
|
|
|
|
|
die(1,"can't parse {}".format(sec))
|
|
|
|
|
if addr_type.name in ('segwit','bech32'):
|
|
|
|
|
hash160_c = key.hash160(is_compressed=True)
|
|
|
|
|
if addr_type.name == 'segwit':
|
|
|
|
|
p2sh_script = network.contract.for_p2pkh_wit(hash160_c)
|
|
|
|
|
addr = network.address.for_p2s(p2sh_script)
|
|
|
|
|
else:
|
|
|
|
|
addr = network.address.for_p2pkh_wit(hash160_c)
|
|
|
|
|
else:
|
|
|
|
|
addr = key.address()
|
|
|
|
|
return gtr(key.wif(),addr,None)
|
|
|
|
|
|
|
|
|
|
class GenToolMoneropy(GenTool):
|
|
|
|
|
desc = 'moneropy'
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
|
|
m = "Unable to import moneropy. Is moneropy installed on your system?"
|
2019-11-01 12:34:51 +00:00
|
|
|
try:
|
2019-11-04 16:48:25 +00:00
|
|
|
import moneropy.account
|
2019-11-01 12:34:51 +00:00
|
|
|
except:
|
2019-11-04 16:48:25 +00:00
|
|
|
raise ImportError(m)
|
|
|
|
|
|
|
|
|
|
self.mpa = moneropy.account
|
2020-05-28 09:53:34 +00:00
|
|
|
proto = init_proto('xmr')
|
2019-11-04 16:48:25 +00:00
|
|
|
|
|
|
|
|
global addr_type
|
2020-05-28 09:53:34 +00:00
|
|
|
addr_type = MMGenAddrType(proto,'M')
|
2015-01-12 00:07:21 +03:00
|
|
|
|
2019-11-06 17:35:14 +00:00
|
|
|
def run(self,sec,vcoin):
|
2019-11-04 16:48:25 +00:00
|
|
|
sk_t,vk_t,addr_t = self.mpa.account_from_spend_key(sec) # VERY slow!
|
|
|
|
|
return gtr(sk_t,addr_t,vk_t)
|
|
|
|
|
|
|
|
|
|
def get_tool(arg):
|
|
|
|
|
|
|
|
|
|
if arg not in ext_progs + ['ext']:
|
2020-05-28 09:53:34 +00:00
|
|
|
die(1,'{!r}: unsupported tool for network {}'.format(arg,proto.network))
|
2019-11-04 16:48:25 +00:00
|
|
|
|
|
|
|
|
if opt.all:
|
|
|
|
|
if arg == 'ext':
|
|
|
|
|
die(1,"'--all' must be combined with a specific external testing tool")
|
|
|
|
|
return arg
|
|
|
|
|
else:
|
|
|
|
|
tool = ci.get_test_support(
|
2020-05-28 09:53:34 +00:00
|
|
|
proto.coin,
|
2019-11-04 16:48:25 +00:00
|
|
|
addr_type.name,
|
2020-05-28 09:53:34 +00:00
|
|
|
proto.network,
|
2019-11-04 16:48:25 +00:00
|
|
|
verbose = not opt.quiet,
|
|
|
|
|
tool = arg if arg in ext_progs else None )
|
|
|
|
|
if not tool:
|
|
|
|
|
sys.exit(2)
|
|
|
|
|
if arg in ext_progs and arg != tool:
|
|
|
|
|
sys.exit(3)
|
|
|
|
|
return tool
|
|
|
|
|
|
|
|
|
|
def test_equal(desc,a_val,b_val,in_bytes,sec,wif,a_desc,b_desc):
|
|
|
|
|
if a_val != b_val:
|
2019-11-02 09:34:21 +00:00
|
|
|
fs = """
|
|
|
|
|
{i:{w}}: {}
|
|
|
|
|
{s:{w}}: {}
|
|
|
|
|
{W:{w}}: {}
|
|
|
|
|
{a:{w}}: {}
|
|
|
|
|
{b:{w}}: {}
|
|
|
|
|
"""
|
|
|
|
|
die(3,
|
2019-11-04 16:48:25 +00:00
|
|
|
red('\nERROR: {} do not match!').format(desc)
|
2019-11-02 09:34:21 +00:00
|
|
|
+ fs.format(
|
2019-11-04 16:48:25 +00:00
|
|
|
in_bytes.hex(), sec, wif, a_val, b_val,
|
|
|
|
|
i='input', s='sec key', W='WIF key', a=a_desc, b=b_desc,
|
|
|
|
|
w=max(len(e) for e in (a_desc,b_desc)) + 1
|
2019-11-02 09:34:21 +00:00
|
|
|
).rstrip())
|
2015-01-12 00:07:21 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
def gentool_test(kg_a,kg_b,ag,rounds):
|
|
|
|
|
|
|
|
|
|
m = "Comparing address generators '{A}' and '{B}' for {N} {c} ({n}), addrtype {a!r}"
|
2020-05-28 09:53:34 +00:00
|
|
|
e = ci.get_entry(proto.coin,proto.network)
|
2019-11-04 16:48:25 +00:00
|
|
|
qmsg(green(m.format(
|
|
|
|
|
A = kg_a.desc,
|
|
|
|
|
B = kg_b.desc,
|
2020-05-28 09:53:34 +00:00
|
|
|
N = proto.network,
|
|
|
|
|
c = proto.coin,
|
2019-11-04 16:48:25 +00:00
|
|
|
n = e.name if e else '---',
|
|
|
|
|
a = addr_type.name )))
|
|
|
|
|
|
2019-11-01 12:31:59 +00:00
|
|
|
global last_t
|
2017-07-07 16:09:28 +03:00
|
|
|
last_t = time.time()
|
|
|
|
|
|
2019-11-01 12:31:59 +00:00
|
|
|
def do_compare_test(n,trounds,in_bytes):
|
|
|
|
|
global last_t
|
2017-12-16 09:31:00 +03:00
|
|
|
if opt.verbose or time.time() - last_t >= 0.1:
|
2019-11-01 12:31:59 +00:00
|
|
|
qmsg_r('\rRound {}/{} '.format(i+1,trounds))
|
2017-07-07 16:09:28 +03:00
|
|
|
last_t = time.time()
|
2020-05-28 09:53:34 +00:00
|
|
|
sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
|
2019-11-02 09:34:21 +00:00
|
|
|
a_ph = kg_a.to_pubhex(sec)
|
|
|
|
|
a_addr = ag.to_addr(a_ph)
|
|
|
|
|
a_vk = None
|
2019-11-04 16:48:25 +00:00
|
|
|
tinfo = (in_bytes,sec,sec.wif,kg_a.desc,kg_b.desc)
|
|
|
|
|
if isinstance(kg_b,GenTool):
|
2019-11-06 17:35:14 +00:00
|
|
|
b = kg_b.run_tool(sec)
|
2019-11-04 16:48:25 +00:00
|
|
|
test_equal('WIF keys',sec.wif,b.wif,*tinfo)
|
|
|
|
|
test_equal('addresses',a_addr,b.addr,*tinfo)
|
|
|
|
|
if b.vk:
|
2019-11-02 09:34:21 +00:00
|
|
|
a_vk = ag.to_viewkey(a_ph)
|
2019-11-04 16:48:25 +00:00
|
|
|
test_equal('view keys',a_vk,b.vk,*tinfo)
|
2017-12-16 09:31:00 +03:00
|
|
|
else:
|
|
|
|
|
b_addr = ag.to_addr(kg_b.to_pubhex(sec))
|
2019-11-04 16:48:25 +00:00
|
|
|
test_equal('addresses',a_addr,b_addr,*tinfo)
|
|
|
|
|
vmsg(fs.format(b=in_bytes.hex(),k=sec.wif,v=a_vk,a=a_addr))
|
2019-11-01 12:31:59 +00:00
|
|
|
qmsg_r('\rRound {}/{} '.format(n+1,trounds))
|
|
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
fs = ( '\ninput: {b}\n%-9s {k}\naddr: {a}\n',
|
|
|
|
|
'\ninput: {b}\n%-9s {k}\nviewkey: {v}\naddr: {a}\n')[
|
|
|
|
|
'viewkey' in addr_type.extra_attrs] % (addr_type.wif_label + ':')
|
2019-11-01 12:31:59 +00:00
|
|
|
|
2019-11-01 12:36:50 +00:00
|
|
|
# test some important private key edge cases:
|
|
|
|
|
edgecase_sks = (
|
|
|
|
|
bytes([0x00]*31 + [0x01]), # min
|
|
|
|
|
bytes([0xff]*32), # max
|
|
|
|
|
bytes([0x0f] + [0xff]*31), # same key as above for zcash-z
|
|
|
|
|
bytes([0x00]*31 + [0xff]), # monero will reduce
|
|
|
|
|
bytes([0xff]*31 + [0x0f]), # monero will not reduce
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
qmsg(purple('edge cases:'))
|
|
|
|
|
for i,in_bytes in enumerate(edgecase_sks):
|
|
|
|
|
do_compare_test(i,len(edgecase_sks),in_bytes)
|
|
|
|
|
qmsg(green('\rOK ' if opt.verbose else 'OK'))
|
|
|
|
|
|
2019-11-01 12:31:59 +00:00
|
|
|
qmsg(purple('random input:'))
|
|
|
|
|
for i in range(rounds):
|
|
|
|
|
do_compare_test(i,rounds,os.urandom(32))
|
|
|
|
|
qmsg(green('\rOK ' if opt.verbose else 'OK'))
|
2017-12-18 20:19:01 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
def speed_test(kg,ag,rounds):
|
2017-12-16 09:31:00 +03:00
|
|
|
m = "Testing speed of address generator '{}' for coin {}"
|
2020-05-28 09:53:34 +00:00
|
|
|
qmsg(green(m.format(kg.desc,proto.coin)))
|
2016-11-11 16:05:27 +03:00
|
|
|
from struct import pack,unpack
|
|
|
|
|
seed = os.urandom(28)
|
2019-11-01 12:24:09 +00:00
|
|
|
qmsg('Incrementing key with each round')
|
|
|
|
|
qmsg('Starting key: {}'.format((seed + pack('I',0)).hex()))
|
2017-07-07 16:09:28 +03:00
|
|
|
import time
|
|
|
|
|
start = last_t = time.time()
|
|
|
|
|
|
2016-11-11 16:05:27 +03:00
|
|
|
for i in range(rounds):
|
2017-07-07 16:09:28 +03:00
|
|
|
if time.time() - last_t >= 0.1:
|
2018-03-07 09:59:35 +00:00
|
|
|
qmsg_r('\rRound {}/{} '.format(i+1,rounds))
|
2017-07-07 16:09:28 +03:00
|
|
|
last_t = time.time()
|
2020-05-28 09:53:34 +00:00
|
|
|
sec = PrivKey(proto,seed+pack('I',i),compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
|
2019-11-04 16:48:25 +00:00
|
|
|
addr = ag.to_addr(kg.to_pubhex(sec))
|
|
|
|
|
vmsg('\nkey: {}\naddr: {}\n'.format(sec.wif,addr))
|
2018-03-07 09:59:35 +00:00
|
|
|
qmsg_r('\rRound {}/{} '.format(i+1,rounds))
|
2017-07-07 16:09:28 +03:00
|
|
|
qmsg('\n{} addresses generated in {:.2f} seconds'.format(rounds,time.time()-start))
|
2017-12-18 20:19:01 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
def dump_test(kg,ag,fh):
|
|
|
|
|
|
|
|
|
|
dump = [[*(e.split()[0] for e in line.split('addr='))] for line in fh.readlines() if 'addr=' in line]
|
|
|
|
|
if not dump:
|
|
|
|
|
die(1,'File {!r} appears not to be a wallet dump'.format(fh.name))
|
|
|
|
|
|
|
|
|
|
m = 'Comparing output of address generator {!r} against wallet dump {!r}'
|
|
|
|
|
qmsg(green(m.format(kg.desc,fh.name)))
|
|
|
|
|
|
|
|
|
|
for count,(b_wif,b_addr) in enumerate(dump,1):
|
|
|
|
|
qmsg_r('\rKey {}/{} '.format(count,len(dump)))
|
2017-08-07 22:02:24 +03:00
|
|
|
try:
|
2020-05-28 09:53:34 +00:00
|
|
|
b_sec = PrivKey(proto,wif=b_wif)
|
2017-08-07 22:02:24 +03:00
|
|
|
except:
|
2020-05-28 09:53:34 +00:00
|
|
|
die(2,'\nInvalid {} WIF address in dump file: {}'.format(proto.network,b_wif))
|
2019-11-04 16:48:25 +00:00
|
|
|
a_addr = ag.to_addr(kg.to_pubhex(b_sec))
|
|
|
|
|
vmsg('\nwif: {}\naddr: {}\n'.format(b_wif,b_addr))
|
|
|
|
|
tinfo = (bytes.fromhex(b_sec),b_sec,b_wif,kg.desc,fh.name)
|
|
|
|
|
test_equal('addresses',a_addr,b_addr,*tinfo)
|
2016-12-08 18:02:28 +03:00
|
|
|
qmsg(green(('\n','')[bool(opt.verbose)] + 'OK'))
|
2017-12-18 20:19:01 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
def init_tool(tname):
|
|
|
|
|
return globals()['GenTool'+capfirst(tname.replace('-','_'))]()
|
2019-10-20 14:20:34 +00:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
def parse_arg1(arg,arg_id):
|
2019-10-20 14:20:34 +00:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
m1 = 'First argument must be a numeric generator ID or two colon-separated generator IDs'
|
|
|
|
|
m2 = 'Second part of first argument must be a numeric generator ID or one of {}'
|
|
|
|
|
|
|
|
|
|
def check_gen_num(n):
|
|
|
|
|
if not (1 <= int(n) <= len(g.key_generators)):
|
|
|
|
|
die(1,'{}: invalid generator ID'.format(n))
|
|
|
|
|
return int(n)
|
|
|
|
|
|
|
|
|
|
if arg_id == 'a':
|
|
|
|
|
if is_int(arg):
|
|
|
|
|
a_num = check_gen_num(arg)
|
2020-05-28 09:53:34 +00:00
|
|
|
return (KeyGenerator(proto,addr_type,a_num),a_num)
|
2017-12-18 20:19:01 +03:00
|
|
|
else:
|
2019-11-04 16:48:25 +00:00
|
|
|
die(1,m1)
|
|
|
|
|
elif arg_id == 'b':
|
|
|
|
|
if is_int(arg):
|
2020-05-28 09:53:34 +00:00
|
|
|
return KeyGenerator(proto,addr_type,check_gen_num(arg))
|
2019-11-04 16:48:25 +00:00
|
|
|
elif arg in ext_progs + ['ext']:
|
|
|
|
|
return init_tool(get_tool(arg))
|
|
|
|
|
else:
|
|
|
|
|
die(1,m2.format(ext_progs))
|
|
|
|
|
|
|
|
|
|
def parse_arg2():
|
|
|
|
|
m = 'Second argument must be dump filename or integer rounds specification'
|
|
|
|
|
if len(cmd_args) == 1:
|
|
|
|
|
return None
|
|
|
|
|
arg = cmd_args[1]
|
|
|
|
|
if is_int(arg) and int(arg) > 0:
|
|
|
|
|
return int(arg)
|
2017-12-18 20:19:01 +03:00
|
|
|
try:
|
2019-11-04 16:48:25 +00:00
|
|
|
return open(arg)
|
2017-12-18 20:19:01 +03:00
|
|
|
except:
|
2019-11-04 16:48:25 +00:00
|
|
|
die(1,m)
|
2017-12-18 20:19:01 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
# begin execution
|
2020-05-16 12:44:43 +00:00
|
|
|
from mmgen.protocol import init_proto
|
2019-11-04 16:48:25 +00:00
|
|
|
from mmgen.altcoin import CoinInfo as ci
|
|
|
|
|
from mmgen.obj import MMGenAddrType,PrivKey
|
2017-12-18 20:19:01 +03:00
|
|
|
from mmgen.addr import KeyGenerator,AddrGenerator
|
|
|
|
|
|
2020-05-28 09:53:34 +00:00
|
|
|
addr_type = MMGenAddrType(
|
|
|
|
|
proto = proto,
|
|
|
|
|
id_str = opt.type or proto.dfl_mmtype )
|
|
|
|
|
ext_progs = list(ci.external_tests[proto.network])
|
2019-11-04 16:48:25 +00:00
|
|
|
|
|
|
|
|
arg1 = cmd_args[0].split(':')
|
|
|
|
|
if len(arg1) == 1:
|
|
|
|
|
a,a_num = parse_arg1(arg1[0],'a')
|
|
|
|
|
b = None
|
|
|
|
|
elif len(arg1) == 2:
|
|
|
|
|
a,a_num = parse_arg1(arg1[0],'a')
|
|
|
|
|
b = parse_arg1(arg1[1],'b')
|
|
|
|
|
else:
|
|
|
|
|
opts.usage()
|
|
|
|
|
|
|
|
|
|
if type(a) == type(b):
|
|
|
|
|
die(1,'Address generators are the same!')
|
|
|
|
|
|
|
|
|
|
arg2 = parse_arg2()
|
|
|
|
|
|
2020-05-28 09:53:34 +00:00
|
|
|
if not opt.all:
|
|
|
|
|
ag = AddrGenerator(proto,addr_type)
|
2017-12-18 20:19:01 +03:00
|
|
|
|
2019-11-04 16:48:25 +00:00
|
|
|
if not b and type(arg2) == int:
|
|
|
|
|
speed_test(a,ag,arg2)
|
|
|
|
|
elif not b and hasattr(arg2,'read'):
|
|
|
|
|
dump_test(a,ag,arg2)
|
|
|
|
|
elif a and b and type(arg2) == int:
|
Key/address generation support for 144 altcoins
Support for these coins is EXPERIMENTAL, use at your own risk
EXAMPLE: generate 10 Dogecoin key/address pairs with your default wallet:
`mmgen-keygen --coin=doge 1-10`
Keys for different coins are distinct, so users needn't worry about key reuse.
Supported alts: 2give,42,611,ac,acoin,alf,anc,apex,arco,arg,aur,b2x,bcf,bch,blk,bmc,bqc,bsty,btcd,btq,bucks,cann,cash,cat,cbx,ccn,cdn,chc,clam,con,cpc,crps,csh,dash,dcr,dfc,dgb,dgc,doge,doged,dope,dvc,efl,emc,emd,enrg,esp,etc,eth,fai,fc2,fibre,fjc,flo,flt,fst,ftc,gcr,good,grc,gun,ham,html5,hyp,icash,infx,inpay,ipc,jbs,judge,lana,lat,ldoge,lmc,ltc,mars,mcar,mec,mint,mobi,mona,moon,mrs,mue,mxt,myr,myriad,mzc,neos,neva,nka,nlg,nmc,nto,nvc,ok,omc,omni,onion,onx,part,pink,pivx,pkb,pnd,pot,ppc,ptc,pxc,qrk,rain,rbt,rby,rdd,ric,sdc,sib,smly,song,spr,start,sys,taj,tit,tpc,trc,ttc,tx,uno,via,vpn,vtc,wash,wdc,wisc,wkc,wsx,xcn,xgb,xmg,xpm,xpoke,xred,xst,xvc,zec,zet,zlq,zoom,zrc
Test the new functionality with `scripts/test-release.sh -Pn master alts`
B2X support disabled pending further testing
2017-12-24 15:26:23 +03:00
|
|
|
if opt.all:
|
2020-05-14 16:40:45 +00:00
|
|
|
from mmgen.protocol import CoinProtocol,init_genonly_altcoins
|
2020-05-28 09:53:34 +00:00
|
|
|
init_genonly_altcoins(testnet=proto.testnet)
|
|
|
|
|
for coin in ci.external_tests[proto.network][b.desc]:
|
2019-11-04 16:48:25 +00:00
|
|
|
if coin.lower() not in CoinProtocol.coins:
|
|
|
|
|
# ymsg('Coin {} not configured'.format(coin))
|
|
|
|
|
continue
|
2020-05-28 09:53:34 +00:00
|
|
|
proto = init_proto(coin)
|
|
|
|
|
if addr_type not in proto.mmtypes:
|
2019-11-04 16:48:25 +00:00
|
|
|
continue
|
2020-05-28 09:53:34 +00:00
|
|
|
# proto has changed, so reinit kg and ag
|
|
|
|
|
a = KeyGenerator(proto,addr_type,a_num)
|
|
|
|
|
ag = AddrGenerator(proto,addr_type)
|
|
|
|
|
b_chk = ci.get_test_support(proto.coin,addr_type.name,proto.network,tool=b.desc,verbose=not opt.quiet)
|
2019-11-04 16:48:25 +00:00
|
|
|
if b_chk == b.desc:
|
|
|
|
|
gentool_test(a,b,ag,arg2)
|
Key/address generation support for 144 altcoins
Support for these coins is EXPERIMENTAL, use at your own risk
EXAMPLE: generate 10 Dogecoin key/address pairs with your default wallet:
`mmgen-keygen --coin=doge 1-10`
Keys for different coins are distinct, so users needn't worry about key reuse.
Supported alts: 2give,42,611,ac,acoin,alf,anc,apex,arco,arg,aur,b2x,bcf,bch,blk,bmc,bqc,bsty,btcd,btq,bucks,cann,cash,cat,cbx,ccn,cdn,chc,clam,con,cpc,crps,csh,dash,dcr,dfc,dgb,dgc,doge,doged,dope,dvc,efl,emc,emd,enrg,esp,etc,eth,fai,fc2,fibre,fjc,flo,flt,fst,ftc,gcr,good,grc,gun,ham,html5,hyp,icash,infx,inpay,ipc,jbs,judge,lana,lat,ldoge,lmc,ltc,mars,mcar,mec,mint,mobi,mona,moon,mrs,mue,mxt,myr,myriad,mzc,neos,neva,nka,nlg,nmc,nto,nvc,ok,omc,omni,onion,onx,part,pink,pivx,pkb,pnd,pot,ppc,ptc,pxc,qrk,rain,rbt,rby,rdd,ric,sdc,sib,smly,song,spr,start,sys,taj,tit,tpc,trc,ttc,tx,uno,via,vpn,vtc,wash,wdc,wisc,wkc,wsx,xcn,xgb,xmg,xpm,xpoke,xred,xst,xvc,zec,zet,zlq,zoom,zrc
Test the new functionality with `scripts/test-release.sh -Pn master alts`
B2X support disabled pending further testing
2017-12-24 15:26:23 +03:00
|
|
|
else:
|
2019-11-04 16:48:25 +00:00
|
|
|
gentool_test(a,b,ag,arg2)
|
2017-12-18 20:19:01 +03:00
|
|
|
else:
|
2019-11-04 16:48:25 +00:00
|
|
|
opts.usage()
|