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
This commit is contained in:
The MMGen Project 2017-12-24 15:26:23 +03:00
commit 91ac2effb3
Signed by: mmgen
GPG key ID: 62DBE9E5212F05BE
21 changed files with 794 additions and 112 deletions

View file

@ -15,7 +15,7 @@ an online and offline computer to provide a robust solution for securely
storing, tracking, sending and receiving Bitcoins.
The online computer is used only for tracking balances and creating and sending
transactions. **Thus it holds no private keys that can be hacked or stolen.**
transactions. Thus it holds no private keys that can be hacked or stolen.
All transactions are signed offline: **your seed and private keys never touch a
network-connected device.** The offline computer used for wallet creation,
address generation and transaction signing is typically a low-powered device

View file

@ -103,7 +103,7 @@ cmd_args = opts.init(opts_data,add_opts=['mmgen_keys_from_file','in_fmt'])
import mmgen.tx
from mmgen.txsign import txsign
from mmgen.protocol import CoinProtocol
from mmgen.protocol import CoinProtocol,init_coin
if opt.stealth_led: opt.led = True
@ -154,8 +154,7 @@ def do_umount():
def sign_tx_file(txfile):
try:
g.coin = mmgen.tx.MMGenTX(txfile,md_only=True).coin
g.proto = CoinProtocol(g.coin,g.testnet)
init_coin(mmgen.tx.MMGenTX(txfile,md_only=True).coin)
reload(sys.modules['mmgen.tx'])
tx = mmgen.tx.MMGenTX(txfile)
rpc_init(reinit=True)

View file

@ -32,7 +32,14 @@ def sc_dmsg(desc,data):
Msg('sc_debug_{}: {}'.format(desc,data))
class AddrGenerator(MMGenObject):
def __new__(cls,gen_method):
def __new__(cls,addr_type):
if type(addr_type) == str: # allow override w/o check
gen_method = addr_type
elif type(addr_type) == MMGenAddrType:
assert addr_type in g.proto.mmtypes,'{}: invalid address type for coin {}'.format(addr_type,g.coin)
gen_method = addr_type.gen_method
else:
raise TypeError,'{}: incorrect argument type for {}()'.format(type(addr_type),cls.__name__)
d = {
'p2pkh': AddrGeneratorP2PKH,
'segwit': AddrGeneratorSegwit,
@ -108,7 +115,14 @@ class AddrGeneratorZcashZ(AddrGenerator):
class KeyGenerator(MMGenObject):
def __new__(cls,pubkey_type,generator=None,silent=False):
def __new__(cls,addr_type,generator=None,silent=False):
if type(addr_type) == str: # allow override w/o check
pubkey_type = addr_type
elif type(addr_type) == MMGenAddrType:
assert addr_type in g.proto.mmtypes,'{}: invalid address type for coin {}'.format(addr_type,g.coin)
pubkey_type = addr_type.pubkey_type
else:
raise TypeError,'{}: incorrect argument type for {}()'.format(type(addr_type),cls.__name__)
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:
@ -330,8 +344,8 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
has_viewkey = self.al_id.mmtype.has_viewkey
if self.gen_addrs:
kg = KeyGenerator(pubkey_type)
ag = AddrGenerator(self.al_id.mmtype.gen_method)
kg = KeyGenerator(self.al_id.mmtype)
ag = AddrGenerator(self.al_id.mmtype)
t_addrs,num,pos,out = len(addrnums),0,0,AddrListList()
le = self.entry_type
@ -529,8 +543,8 @@ 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(self.al_id.mmtype.pubkey_type)
ag = AddrGenerator(self.al_id.mmtype.gen_method)
kg = KeyGenerator(self.al_id.mmtype)
ag = AddrGenerator(self.al_id.mmtype)
llen = len(ret)
for n,e in enumerate(ret):
msg_r('\rVerifying keys %s/%s' % (n+1,llen))

529
mmgen/altcoin.py Executable file
View file

@ -0,0 +1,529 @@
#!/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/>.
"""
altcoin.py - Coin constants for Bitcoin-derived altcoins
"""
# Sources:
# lb: https://github.com/libbitcoin/libbitcoin/wiki/Altcoin-Version-Mappings
# pc: https://github.com/richardkiss/pycoin/blob/master/pycoin/networks/legacy_networks.py
# vg: https://github.com/exploitagency/vanitygen-plus/blob/master/keyconv.c
# wn: https://walletgenerator.net
# cc: https://www.cryptocompare.com/api/data/coinlist/ (names,symbols only)
# WIP:
# NSR: 149/191 c/u, 63/('S'), 64/('S'|'T')
# NBT: 150/191 c/u, 25/('B'), 26/('B')
import sys
def msg(s): sys.stderr.write(s+'\n')
class CoinInfo(object):
coin_constants = {}
coin_constants['mainnet'] = (
# NAME SYM WIF P2PKH P2SH SEGWIT TRUST
# trust levels: 0=untested 1=low 2=med 3=high
('Bitcoin', 'BTC', 0x80, (0x00,'1'), (0x05,'3'), True, 3),
('BitcoinSegwit2X', 'B2X', 0x80, (0x00,'1'), (0x05,'3'), True, 2),
('Bcash', 'BCH', 0x80, (0x00,'1'), (0x05,'3'), False, 3),
('2GiveCoin', '2GIVE', 0xa7, (0x27,('G','H')), None, False, 0),
('42Coin', '42', 0x88, (0x08,'4'), None, False, 1),
('ACoin', 'ACOIN', 0xe6, (0x17,'A'), None, False, 0),
('Alphacoin', 'ALF', 0xd2, (0x52,('Z','a')), None, False, 0),
('Anoncoin', 'ANC', 0x97, (0x17,'A'), None, False, 1),
('Apexcoin', 'APEX', 0x97, (0x17,'A'), None, False, 0),
('Aquariuscoin', 'ARCO', 0x97, (0x17,'A'), None, False, 0),
('Argentum', 'ARG', 0x97, (0x17,'A'), (0x05,'3'), False, 1),
('AsiaCoin', 'AC', 0x97, (0x17,'A'), (0x08,'4'), False, 1),
('Auroracoin', 'AUR', 0x97, (0x17,'A'), None, False, 1),
('BBQcoin', 'BQC', 0xd5, (0x55,'b'), None, False, 1),
('BitcoinDark', 'BTCD', 0xbc, (0x3c,'R'), (0x55,'b'), False, 1),
('BitcoinFast', 'BCF', 0xe0, (0x60,('f','g')), None, False, 0),
('BitQuark', 'BTQ', 0xba, (0x3a,'Q'), None, False, 0),
('Blackcoin', 'BLK', 0x99, (0x19,'B'), (0x55,'b'), False, 1),
('BlackmoonCrypto', 'BMC', 0x83, (0x03,'2'), None, False, 0),
('BlockCat', 'CAT', 0x95, (0x15,'9'), None, False, 0),
('CanadaECoin', 'CDN', 0x9c, (0x1c,'C'), (0x05,'3'), False, 1),
('CannabisCoin', 'CANN', 0x9c, (0x1c,'C'), None, False, 0),
('CannaCoin', 'CCN', 0x9c, (0x1c,'C'), (0x05,'3'), False, 1),
('Capricoin', 'CPC', 0x9c, (0x1c,'C'), None, False, 0),
('CashCoin', 'CASH', 0xa2, (0x22,('E','F')), None, False, 0),
('CashOut', 'CSH', 0xa2, (0x22,('E','F')), None, False, 0),
('ChainCoin', 'CHC', 0x9c, (0x1c,'C'), None, False, 0),
('Clams', 'CLAM', 0x85, (0x89,'x'), (0x0d,'6'), False, 1),
('CoinMagi', 'XMG', 0x94, (0x14,'9'), None, False, 0),
('Condensate', 'RAIN', 0xbc, (0x3c,'R'), None, False, 0),
('CryptoBullion', 'CBX', 0x8b, (0x0b,'5'), None, False, 0),
('Cryptonite', 'XCN', 0x80, (0x1c,'C'), None, False, 0),
('CryptoPennies', 'CRPS', 0xc2, (0x42,'T'), None, False, 0),
('Dash', 'DASH', 0xcc, (0x4c,'X'), (0x10,'7'), False, 1),
('Decred', 'DCR', 0x22de, (0x073f,'D'), (0x071a,'D'), False, 1),
('DeepOnion', 'ONION', 0x9f, (0x1f,'D'), None, False, 1),
('Defcoin', 'DFC', 0x9e, (0x1e,'D'), (0x05,'3'), False, 1),
('Devcoin', 'DVC', 0x80, (0x00,'1'), None, False, 1),
('DigiByte', 'DGB', 0x80, (0x1e,'D'), (0x05,'3'), False, 1),
('DigiCoin', 'DGC', 0x9e, (0x1e,'D'), (0x05,'3'), False, 1),
('DogecoinDark', 'DOGED', 0x9e, (0x1e,'D'), (0x21,'E'), False, 1),
('Dogecoin', 'DOGE', 0x9e, (0x1e,'D'), (0x16,('9','A')), False, 2),
('DopeCoin', 'DOPE', 0x88, (0x08,'4'), (0x05,'3'), False, 1),
('EGulden', 'EFL', 0xb0, (0x30,'L'), (0x05,'3'), False, 1),
('Emerald', 'EMD', 0xa2, (0x22,('E','F')), None, False, 0),
('Emercoin', 'EMC', 0x80, (0x21,'E'), (0x5c,'e'), False, 2),
('EnergyCoin', 'ENRG', 0xdc, (0x5c,'e'), None, False, 0),
('Espers', 'ESP', 0xa1, (0x21,'E'), None, False, 0),
('Faircoin', 'FAI', 0xdf, (0x5f,'f'), (0x24,'F'), False, 1),
('Fastcoin', 'FST', 0xe0, (0x60,('f','g')), None, False, 0),
('Feathercoin', 'FTC', 0x8e, (0x0e,('6','7')), (0x05,'3'), False, 2),
('Fibre', 'FIBRE', 0xa3, (0x23,'F'), None, False, 0),
('FlorinCoin', 'FLO', 0xb0, (0x23,'F'), None, False, 0),
('Fluttercoin', 'FLT', 0xa3, (0x23,'F'), None, False, 0),
('Fuel2Coin', 'FC2', 0x80, (0x24,'F'), None, False, 0),
('Fujicoin', 'FJC', 0xa4, (0x24,'F'), None, False, 0),
('Fujinto', 'NTO', 0xa4, (0x24,'F'), None, False, 0),
('GlobalBoost', 'BSTY', 0xa6, (0x26,'G'), None, False, 0),
('GlobalCurrencyReserve', 'GCR', 0x9a, (0x26,'G'), (0x61,'g'), False, 1),
('GoldenBird', 'XGB', 0xaf, (0x2f,('K','L')), None, False, 0),
('Goodcoin', 'GOOD', 0xa6, (0x26,'G'), None, False, 0),
('GridcoinResearch', 'GRC', 0xbe, (0x3e,('R','S')), None, False, 1),
('Gulden', 'NLG', 0xa6, (0x26,'G'), None, False, 1),
('Guncoin', 'GUN', 0xa7, (0x27,('G','H')), None, False, 1),
('HamRadioCoin', 'HAM', 0x80, (0x00,'1'), None, False, 1),
('HTML5Coin', 'HTML5', 0xa8, (0x28,'H'), None, False, 0),
('HyperStake', 'HYP', 0xf5, (0x75,'p'), None, False, 0),
('iCash', 'ICASH', 0xcc, (0x66,'i'), None, False, 0),
('ImperiumCoin', 'IPC', 0xb0, (0x30,'L'), None, False, 0),
('IncaKoin', 'NKA', 0xb5, (0x35,'N'), None, False, 0),
('Influxcoin', 'INFX', 0xe6, (0x66,'i'), None, False, 0),
('InPay', 'INPAY', 0xb7, (0x37,'P'), None, False, 0),
# ('iXcoin', 'IXC', 0x80, (0x8a,'x'), None, False, 1),
('Judgecoin', 'JUDGE', 0xab, (0x2b,'J'), None, False, 0),
('Jumbucks', 'JBS', 0xab, (0x2b,'J'), (0x69,'j'), False, 2),
('Lanacoin', 'LANA', 0xb0, (0x30,'L'), None, False, 0),
('Latium', 'LAT', 0x80, (0x17,'A'), None, False, 0),
('Litecoin', 'LTC', 0xb0, (0x30,'L'), (0x05,'3'), True, 3),
('LiteDoge', 'LDOGE', 0xab, (0x5a,'d'), None, False, 0),
('LomoCoin', 'LMC', 0xb0, (0x30,'L'), None, False, 0),
('Marscoin', 'MARS', 0xb2, (0x32,'M'), None, False, 0),
('MarsCoin', 'MRS', 0xb2, (0x32,'M'), None, False, 0),
('MartexCoin', 'MXT', 0xb2, (0x32,'M'), None, False, 0),
('MasterCar', 'MCAR', 0xe6, (0x17,'A'), None, False, 0),
('MazaCoin', 'MZC', 0xe0, (0x32,'M'), (0x09,('4','5')), False, 2),
('MegaCoin', 'MEC', 0xb2, (0x32,'M'), None, False, 1),
('MintCoin', 'MINT', 0xb3, (0x33,'M'), None, False, 0),
('Mobius', 'MOBI', 0x80, (0x00,'1'), None, False, 0),
('MonaCoin', 'MONA', 0xb0, (0x32,'M'), (0x05,'3'), False, 1),
('MonetaryUnit', 'MUE', 0x8f, (0x0f,'7'), (0x09,('4','5')), False, 1),
('MoonCoin', 'MOON', 0x83, (0x03,'2'), None, False, 0),
('MyriadCoin', 'MYR', 0xb2, (0x32,'M'), (0x09,('4','5')), False, 1),
('Myriadcoin', 'MYRIAD', 0xb2, (0x32,'M'), None, False, 1),
('Namecoin', 'NMC', 0xb4, (0x34,('M','N')), (0x0d,'6'), False, 1),
('Neoscoin', 'NEOS', 0xef, (0x3f,'S'), (0xbc,'2'), False, 1),
('NevaCoin', 'NEVA', 0xb1, (0x35,'N'), None, False, 0),
('Novacoin', 'NVC', 0x88, (0x08,'4'), (0x14,'9'), False, 1),
('OKCash', 'OK', 0xb7, (0x37,'P'), (0x1c,'C'), False, 1),
('Omnicoin', 'OMC', 0xf3, (0x73,'o'), None, False, 1),
('Omni', 'OMNI', 0xf3, (0x73,'o'), None, False, 0),
('Onix', 'ONX', 0x80, (0x8a,'x'), None, False, 0),
('PandaCoin', 'PND', 0xb7, (0x37,'P'), (0x16,('9','A')), False, 1),
('ParkByte', 'PKB', 0xb7, (0x37,'P'), (0x1c,'C'), False, 1),
('Particl', 'PART', 0x6c, (0x38,'P'), None, False, 0),
('Paycoin', 'CON', 0xb7, (0x37,'P'), None, False, 1),
('Peercoin', 'PPC', 0xb7, (0x37,'P'), (0x75,'p'), False, 1),
('PesetaCoin', 'PTC', 0xaf, (0x2f,('K','L')), None, False, 1),
('PhoenixCoin', 'PXC', 0xb8, (0x38,'P'), None, False, 0),
('PinkCoin', 'PINK', 0x83, (0x03,'2'), None, False, 1),
('PIVX', 'PIVX', 0xd4, (0x1e,'D'), None, False, 0),
('PokeChain', 'XPOKE', 0x9c, (0x1c,'C'), None, False, 0),
('Potcoin', 'POT', 0xb7, (0x37,'P'), (0x05,'3'), False, 1),
('Primecoin', 'XPM', 0x97, (0x17,'A'), (0x53,'a'), False, 1),
('Quark', 'QRK', 0xba, (0x3a,'Q'), None, False, 0),
('ReddCoin', 'RDD', 0xbd, (0x3d,'R'), None, False, 1),
('Riecoin', 'RIC', 0x80, (0x3c,'R'), (0x05,'3'), False, 2),
('Rimbit', 'RBT', 0xbc, (0x3c,'R'), None, False, 0),
('Rubycoin', 'RBY', 0xbd, (0x3d,'R'), (0x55,'b'), False, 1),
('ShadowCash', 'SDC', 0xbf, (0x3f,'S'), (0x7d,'s'), False, 1),
('Sibcoin', 'SIB', 0x80, (0x3f,'S'), None, False, 0),
('SixEleven', '611', 0x80, (0x34,('M','N')), None, False, 0),
('SmileyCoin', 'SMLY', 0x99, (0x19,'B'), None, False, 0),
('Songcoin', 'SONG', 0xbf, (0x3f,'S'), None, False, 0),
('Spreadcoin', 'SPR', 0xbf, (0x3f,'S'), None, False, 1),
('Startcoin', 'START', 0xfd, (0x7d,'s'), (0x05,'3'), False, 1),
('StealthCoin', 'XST', 0xbe, (0x3e,('R','S')), None, False, 0),
('SwagBucks', 'BUCKS', 0x99, (0x3f,'S'), None, False, 0),
('SysCoin', 'SYS', 0x80, (0x00,'1'), None, False, 0),
('TajCoin', 'TAJ', 0x6f, (0x41,'T'), None, False, 0),
('Templecoin', 'TPC', 0xc1, (0x41,'T'), (0x05,'3'), False, 1),
('Terracoin', 'TRC', 0x80, (0x00,'1'), None, False, 0),
('Titcoin', 'TIT', 0x80, (0x00,'1'), None, False, 0),
('TittieCoin', 'TTC', 0xc1, (0x41,'T'), None, False, 0),
('Transfer', 'TX', 0x99, (0x42,'T'), None, False, 0),
('Unobtanium', 'UNO', 0xe0, (0x82,'u'), (0x1e,'D'), False, 2),
('Vcash', 'XVC', 0xc7, (0x47,'V'), None, False, 0),
('Vertcoin', 'VTC', 0xc7, (0x47,'V'), (0x05,'3'), False, 1),
('Viacoin', 'VIA', 0xc7, (0x47,'V'), (0x21,'E'), False, 2),
('VpnCoin', 'VPN', 0xc7, (0x47,'V'), (0x05,'3'), False, 1),
('WankCoin', 'WKC', 0x80, (0x00,'1'), None, False, 1),
('WashingtonCoin', 'WASH', 0xc9, (0x49,'W'), None, False, 0),
('WeAreSatoshi', 'WSX', 0x97, (0x87,'w'), None, False, 0),
('WisdomCoin', 'WISC', 0x87, (0x49,'W'), None, False, 0),
('WorldCoin', 'WDC', 0xc9, (0x49,'W'), None, False, 1),
('XRealEstateDevcoin', 'XRED', 0x80, (0x00,'1'), None, False, 0),
('ZetaCoin', 'ZET', 0xe0, (0x50,'Z'), None, False, 0),
('ZiftrCoin', 'ZRC', 0xd0, (0x50,'Z'), (0x05,'3'), False, 1),
('ZLiteQubit', 'ZLQ', 0xe0, (0x26,'G'), None, False, 0),
('Zoomcoin', 'ZOOM', 0xe7, (0x67,'i'), (0x5c,'e'), False, 1),
)
coin_constants['testnet'] = (
('Dash', 'DASH', 0xef, (0x8c,'y'), (0x13,('8','9')), False, 1),
('Decred', 'DCR', 0x230e, (0x0f21,'T'), (0x0e6c,'S'), False, 1),
('Dogecoin', 'DOGE', 0xf1, (0x71,'n'), (0xc4,'2'), False, 2),
('Feathercoin', 'FTC', 0xc1, (0x41,'T'), (0xc4,'2'), False, 2),
('Viacoin', 'VIA', 0xff, (0x7f,'t'), (0xc4,'2'), False, 2),
('Emercoin', 'EMC', 0xef, (0x6f,('m','n')), (0xc4,'2'), False, 2),
)
coin_sources = (
('BTC', 'https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp'),
('EMC', 'https://github.com/emercoin/emercoin/blob/master/src/chainparams.cpp'), # checked mn,tn
('LTC', 'https://github.com/litecoin-project/litecoin/blob/master-0.10/src/chainparams.cpp'),
('DOGE', 'https://github.com/dogecoin/dogecoin/blob/master/src/chainparams.cpp'),
('RDD', 'https://github.com/reddcoin-project/reddcoin/blob/master/src/base58.h'),
('DASH', 'https://github.com/dashpay/dash/blob/master/src/chainparams.cpp'),
('PPC', 'https://github.com/belovachap/peercoin/blob/master/src/base58.h'),
('NMC', 'https://github.com/domob1812/namecore/blob/master/src/chainparams.cpp'),
('FTC', 'https://github.com/FeatherCoin/Feathercoin/blob/master-0.8/src/base58.h'),
('BLK', 'https://github.com/rat4/blackcoin/blob/master/src/chainparams.cpp'),
('NSR', 'https://nubits.com/nushares/introduction'),
('NBT', 'https://bitbucket.org/JordanLeePeershares/nubit/NuBit / src /base58.h'),
('MZC', 'https://github.com/MazaCoin/MazaCoin/blob/master/src/chainparams.cpp'),
('VIA', 'https://github.com/viacoin/viacoin/blob/master/src/chainparams.cpp'),
('RBY', 'https://github.com/rubycoinorg/rubycoin/blob/master/src/base58.h'),
('GRS', 'https://github.com/GroestlCoin/groestlcoin/blob/master/src/groestlcoin.cpp'),
('DGC', 'https://github.com/DGCDev/digitalcoin/blob/master/src/chainparams.cpp'),
('CCN', 'https://github.com/Cannacoin-Project/Cannacoin/blob/Proof-of-Stake/src/base58.h'),
('DGB', 'https://github.com/digibyte/digibyte/blob/master/src/chainparams.cpp'),
('MONA', 'https://github.com/monacoinproject/monacoin/blob/master-0.10/src/chainparams.cpp'),
('CLAM', 'https://github.com/nochowderforyou/clams/blob/master/src/chainparams.cpp'),
('XPM', 'https://github.com/primecoin/primecoin/blob/master/src/base58.h'),
('NEOS', 'https://github.com/bellacoin/neoscoin/blob/master/src/chainparams.cpp'),
('JBS', 'https://github.com/jyap808/jumbucks/blob/master/src/base58.h'),
('ZRC', 'https://github.com/ZiftrCOIN/ziftrcoin/blob/master/src/chainparams.cpp'),
('VTC', 'https://github.com/vertcoin/vertcoin/blob/master/src/base58.h'),
('NXT', 'https://bitbucket.org/JeanLucPicard/nxt/src and unofficial at https://github.com/Blackcomb/nxt'),
('MUE', 'https://github.com/MonetaryUnit/MUE-Src/blob/master/src/chainparams.cpp'),
('ZOOM', 'https://github.com/zoom-c/zoom/blob/master/src/base58.h'),
('VPN', 'https://github.com/Bit-Net/VpnCoin/blob/master/src/base58.h'),
('CDN', 'https://github.com/ThisIsOurCoin/canadaecoin/blob/master/src/base58.h'),
('SDC', 'https://github.com/ShadowProject/shadow/blob/master/src/chainparams.cpp'),
('PKB', 'https://github.com/parkbyte/ParkByte/blob/master/src/base58.h'),
('PND', 'https://github.com/coinkeeper/2015-04-19_21-22_pandacoin/blob/master/src/base58.h'),
('START', 'https://github.com/startcoin-project/startcoin/blob/master/src/base58.h'),
('GCR', 'https://github.com/globalcurrencyreserve/gcr/blob/master/src/chainparams.cpp'),
('NVC', 'https://github.com/novacoin-project/novacoin/blob/master/src/base58.h'),
('AC', 'https://github.com/AsiaCoin/AsiaCoinFix/blob/master/src/base58.h'),
('BTCD', 'https://github.com/jl777/btcd/blob/master/src/base58.h'),
('DOPE', 'https://github.com/dopecoin-dev/DopeCoinV3/blob/master/src/base58.h'),
('TPC', 'https://github.com/9cat/templecoin/blob/templecoin/src/base58.h'),
('OK', 'https://github.com/okcashpro/okcash/blob/master/src/chainparams.cpp'),
('DOGED', 'https://github.com/doged/dogedsource/blob/master/src/base58.h'),
('EFL', 'https://github.com/Electronic-Gulden-Foundation/egulden/blob/master/src/base58.h'),
('POT', 'https://github.com/potcoin/Potcoin/blob/master/src/base58.h'),
)
# Sources (see above) that are in agreement for these coins
# No check for segwit, p2sh check skipped if source doesn't support it
cross_checks = {
'2GIVE': ['wn'],
'42': ['vg','wn'],
'611': ['wn'],
'AC': ['lb','vg'],
'ACOIN': ['wn'],
'ALF': ['wn'],
'ANC': ['vg','wn'],
'APEX': ['wn'],
'ARCO': ['wn'],
'ARG': ['pc'],
'AUR': ['vg','wn'],
'BCH': ['wn'],
'BLK': ['lb','vg','wn'],
'BQC': ['vg','wn'],
'BSTY': ['wn'],
'BTC': ['lb','vg','wn'],
'BTCD': ['lb','vg','wn'],
'BUCKS': ['wn'],
'CASH': ['wn'],
'CBX': ['wn'],
'CCN': ['lb','vg','wn'],
'CDN': ['lb','vg','wn'],
'CHC': ['wn'],
'CLAM': ['lb','vg'],
'CON': ['vg','wn'],
'CPC': ['wn'],
'DASH': ['lb','pc','vg','wn'],
'DCR': ['pc'],
'DFC': ['pc'],
'DGB': ['lb','vg'],
'DGC': ['lb','vg','wn'],
'DOGE': ['lb','pc','vg','wn'],
'DOGED': ['lb','vg','wn'],
'DOPE': ['lb','vg'],
'DVC': ['vg','wn'],
'EFL': ['lb','vg','wn'],
'EMC': ['vg'],
'EMD': ['wn'],
'ESP': ['wn'],
'FAI': ['pc'],
'FC2': ['wn'],
'FIBRE': ['wn'],
'FJC': ['wn'],
'FLO': ['wn'],
'FLT': ['wn'],
'FST': ['wn'],
'FTC': ['lb','pc','vg','wn'],
'GCR': ['lb','vg'],
'GOOD': ['wn'],
'GRC': ['vg','wn'],
'GUN': ['vg','wn'],
'HAM': ['vg','wn'],
'HTML5': ['wn'],
'HYP': ['wn'],
'ICASH': ['wn'],
'INFX': ['wn'],
'IPC': ['wn'],
'JBS': ['lb','pc','vg','wn'],
'JUDGE': ['wn'],
'LANA': ['wn'],
'LAT': ['wn'],
'LDOGE': ['wn'],
'LMC': ['wn'],
'LTC': ['lb','vg','wn'],
'MARS': ['wn'],
'MEC': ['pc','wn'],
'MINT': ['wn'],
'MOBI': ['wn'],
'MONA': ['lb','vg'],
'MOON': ['wn'],
'MUE': ['lb','vg'],
'MXT': ['wn'],
'MYR': ['pc'],
'MYRIAD': ['vg','wn'],
'MZC': ['lb','pc','vg','wn'],
'NEOS': ['lb','vg'],
'NEVA': ['wn'],
'NKA': ['wn'],
'NLG': ['vg','wn'],
'NMC': ['lb','vg'],
'NVC': ['lb','vg','wn'],
'OK': ['lb','vg'],
'OMC': ['vg','wn'],
'ONION': ['vg','wn'],
'PART': ['wn'],
'PINK': ['vg','wn'],
'PIVX': ['wn'],
'PKB': ['lb','vg','wn'],
'PND': ['lb','vg','wn'],
'POT': ['lb','vg','wn'],
'PPC': ['lb','vg','wn'],
'PTC': ['vg','wn'],
'PXC': ['wn'],
'QRK': ['wn'],
'RAIN': ['wn'],
'RBT': ['wn'],
'RBY': ['lb','vg'],
'RDD': ['vg','wn'],
'RIC': ['pc','vg','wn'],
'SDC': ['lb','vg'],
'SIB': ['wn'],
'SMLY': ['wn'],
'SONG': ['wn'],
'SPR': ['vg','wn'],
'START': ['lb','vg'],
'SYS': ['wn'],
'TAJ': ['wn'],
'TIT': ['wn'],
'TPC': ['lb','vg'],
'TRC': ['wn'],
'TTC': ['wn'],
'TX': ['wn'],
'UNO': ['pc','vg','wn'],
'VIA': ['lb','pc','vg','wn'],
'VPN': ['lb','vg'],
'VTC': ['lb','vg','wn'],
'WDC': ['vg','wn'],
'WISC': ['wn'],
'WKC': ['vg','wn'],
'WSX': ['wn'],
'XCN': ['wn'],
'XGB': ['wn'],
'XPM': ['lb','vg','wn'],
'XST': ['wn'],
'XVC': ['wn'],
'ZET': ['wn'],
'ZOOM': ['lb','vg'],
'ZRC': ['lb','vg']
}
# data is one of the coin_constants lists above
# normalize ints to hex, format width, add missing leading letters, set trust level from external_tests
# Insert a coin entry from outside source, set leading letters to '?' and trust to 0, then run fix_table()
# segwit column is updated manually for now
@classmethod
def fix_table(cls,data):
import re
def myhex(n):
return '0x{:0{}x}'.format(n,2 if n < 256 else 4)
def fix_col(line,n):
line[n] = list(line[n])
line[n][0] = myhex(line[n][0])
s1 = cls.find_addr_leading_symbol(int(line[n][0][2:],16))
m = 'Fixing coin {} [in data: {!r}] [computed: {}]'.format(line[0],line[n][1],s1)
if line[n][1] != '?':
assert s1 == line[n][1],'First letters do not match! {}'.format(m)
else:
msg(m)
line[n][1] = s1
line[n] = tuple(line[n])
old_sym = None
for sym in sorted([e[1] for e in data]):
if sym == old_sym:
msg("'{}': duplicate coin symbol in data!".format(sym))
sys.exit(2)
old_sym = sym
tt = cls.create_trust_table()
w = max(len(e[0]) for e in data)
fs = '\t({{:{}}} {{:10}} {{:7}} {{:17}} {{:17}} {{:6}} {{}}),'.format(w+3)
for line in data:
line = list(line)
line[2] = myhex(line[2])
fix_col(line,3)
if type(line[4]) == tuple: fix_col(line,4)
sym,trust = line[1],line[6]
for n in range(len(line)):
line[n] = repr(line[n])
line[n] = re.sub(r"'0x(..)'",r'0x\1',line[n])
line[n] = re.sub(r"'0x(....)'",r'0x\1',line[n])
line[n] = re.sub(r' ',r'',line[n]) + ('',',')[n != len(line)-1]
from mmgen.util import pmsg,pdie
# pmsg(sym)
# pdie(tt)
if sym in tt:
src = tt[sym]
if src != trust:
msg("Updating trust for coin '{}': {} -> {}".format(sym,trust,src))
line[6] = src
else:
if trust != 0:
msg("Downgrading trust for coin '{}': {} -> {}".format(sym,trust,0))
line[6] = 0
if sym in cls.cross_checks:
if int(line[6]) == 0 and len(cls.cross_checks[sym]) > 1:
msg("Upgrading trust for coin '{}': {} -> {}".format(sym,line[6],1))
line[6] = 1
print(fs.format(*line))
msg('Processed {} entries'.format(len(data)))
@classmethod
def find_addr_leading_symbol(cls,ver_num,verbose=False):
def phash2addr(ver_num,pk_hash):
from mmgen.protocol import _b58chk_encode
s = '{:0{}x}'.format(ver_num,2 if ver_num < 256 else 4) + pk_hash
lzeroes = (len(s) - len(s.lstrip('0'))) / 2 # non-zero only for ver num '00' (BTC p2pkh)
return ('1' * lzeroes) + _b58chk_encode(s)
low = phash2addr(ver_num,'00'*20)
high = phash2addr(ver_num,'ff'*20)
if verbose:
print('low address: ' + low)
print('high address: ' + high)
l1,h1 = low[0],high[0]
return (l1,h1) if l1 != h1 else l1
@classmethod
def print_symbols(cls,include_names=False,reverse=False):
w = max(len(e[0]) for e in cls.coin_constants['mainnet'])
for line in cls.coin_constants['mainnet']:
if reverse:
print('{:6} {}'.format(line[1],line[0]))
else:
print(('','{:{}} '.format(line[0],w))[include_names] + line[1])
@classmethod
def create_trust_table(cls):
tt = {}
mn = cls.external_tests['mainnet']
for ext_prog in mn:
assert len(set(mn[ext_prog])) == len(mn[ext_prog]),"Duplicate entry in '{}'!".format(ext_prog)
for coin in mn[ext_prog]:
if coin in tt:
tt[coin] += 1
else:
tt[coin] = 1
for k in cls.trust_override:
tt[k] = cls.trust_override[k]
return tt
trust_override = {'BTC':3,'BCH':3,'LTC':3,'DASH':1,'EMC':2}
external_tests = {
'mainnet': {
'pycoin': (
# broken: DASH - only compressed, LTC segwit old fmt
'BTC','LTC','VIA','FTC','DOGE','MEC','MYR','UNO',
'JBS','MZC','RIC','DFC','FAI','ARG','ZEC','DCR'),
'pyethereum': ('ETH','ETC'),
'zcash_mini': ('ZEC',),
'keyconv': ( # all supported by vanitygen-plus 'keyconv' util
# broken: PIVX
'42','AC','AIB','ANC','ARS','ATMOS','AUR','BLK','BQC','BTC','TEST','BTCD','CCC','CCN','CDN',
'CLAM','CNC','CNOTE','CON','CRW','DEEPONION','DGB','DGC','DMD','DOGED','DOGE','DOPE',
'DVC','EFL','EMC','EXCL','FAIR','FLOZ','FTC','GAME','GAP','GCR','GRC','GRS','GUN','HAM','HODL',
'IXC','JBS','LBRY','LEAF','LTC','MMC','MONA','MUE','MYRIAD','MZC','NEOS','NLG','NMC','NVC',
'NYAN','OK','OMC','PIGGY','PINK','PKB','PND','POT','PPC','PTC','PTS','QTUM','RBY','RDD',
'RIC','SCA','SDC','SKC','SPR','START','SXC','TPC','UIS','UNO','VIA','VPN','VTC','WDC','WKC',
'WUBS', 'XC', 'XPM', 'YAC', 'ZOOM', 'ZRC')
},
'testnet': {
'pycoin': {
# broken: DASH - only compressed { 'DASH':'tDASH' }
'BTC':'XTN','LTC':'XLT','VIA':'TVI','FTC':'FTX','DOGE':'XDT','DCR':'DCRT'
},
'pyethereum': {},
'keyconv': {}
}
}
external_tests_segwit_compressed = {
'segwit': ('BTC'),
'compressed': (
'BTC','LTC','VIA','FTC','DOGE','DASH','MEC','MYR','UNO',
'JBS','MZC','RIC','DFC','FAI','ARG','ZEC','DCR','ZEC'),
}

View file

@ -51,3 +51,9 @@ def launch(what):
sys.stderr.write('\nUser interrupt\n')
except EOFError:
sys.stderr.write('\nEnd of file\n')
except Exception as e:
if os.getenv('MMGEN_TRACEBACK'):
raise
else:
sys.stderr.write('{!r}\n'.format(e[0]))
sys.exit(2)

View file

@ -96,12 +96,11 @@ if mmids[0] == mmids[1]:
die(2,'Both transactions have the same output! ({})'.format(mmids[0]))
from mmgen.tx import MMGenSplitTX
from mmgen.protocol import CoinProtocol
from mmgen.protocol import init_coin
if opt.tx_fees:
for idx,g_coin in ((1,opt.other_coin),(0,g.coin)):
g.coin = g_coin
g.proto = CoinProtocol(g.coin,g.testnet)
init_coin(g_coin)
opt.tx_fee = opt.tx_fees.split(',')[idx]
opts.opt_is_tx_fee(opt.tx_fee,'transaction fee') or sys.exit(1)
@ -119,9 +118,7 @@ tx1.create_fn()
gmsg("\nCreating transaction for short chain ({})".format(opt.other_coin))
from mmgen.protocol import CoinProtocol
g.coin = opt.other_coin
g.proto = CoinProtocol(g.coin,g.testnet)
init_coin(opt.other_coin)
reload(sys.modules['mmgen.tx'])
tx2 = MMGenSplitTX()

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 (use 'pubkey_type=zcash_z' for zcash-z key)
hex2wif - convert a private key from hex to WIF format (use '--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

@ -48,7 +48,7 @@ if not opt.status: do_license_msg()
from mmgen.tx import *
tx = MMGenTX(infile) # sig check performed here
tx = MMGenTX(infile,silent_open=True) # sig check performed here
vmsg("Signed transaction file '%s' is valid" % infile)
if not tx.marked_signed():

View file

@ -170,9 +170,23 @@ def override_from_env():
gname = name[idx:].lower()
setattr(g,gname,set_for_type(val,getattr(g,gname),name,invert_bool))
def warn_altcoins(trust_level):
if trust_level == None: return
tl = (red('COMPLETELY UNTESTED'),red('LOW'),yellow('MEDIUM'),green('HIGH'))
m = """
Support for coin '{}' is EXPERIMENTAL. The {pn} project assumes no
responsibility for any loss of funds you may incur.
This coin's {pn} testing status: {}
Are you sure you want to continue?
""".strip().format(g.coin,tl[trust_level],pn=g.proj_name)
if os.getenv('MMGEN_TEST_SUITE'):
msg(m); return
if not keypress_confirm(m):
sys.exit(0)
def init(opts_f,add_opts=[],opt_filter=None):
from mmgen.protocol import CoinProtocol,BitcoinProtocol
from mmgen.protocol import CoinProtocol,BitcoinProtocol,init_genonly_altcoins
g.proto = BitcoinProtocol # this must be initialized to something before opts_f is called
# most, but not all, of these set the corresponding global var
@ -239,6 +253,8 @@ def init(opts_f,add_opts=[],opt_filter=None):
if g.regtest: g.testnet = True # These are equivalent for now
altcoin_trust_level = init_genonly_altcoins(opt.coin)
# g.testnet is set, so we can set g.proto
g.proto = CoinProtocol(g.coin,g.testnet)
@ -300,6 +316,8 @@ def init(opts_f,add_opts=[],opt_filter=None):
for k in ('prog_name','desc','usage','options','notes'):
if k in opts_data: del opts_data[k]
warn_altcoins(altcoin_trust_level)
return args
def opt_is_tx_fee(val,desc):

View file

@ -84,10 +84,9 @@ class BitcoinProtocol(MMGenObject):
(None,'','b2x',True)
]
caps = ('rbf','segwit')
mmcaps = ('key','addr','rpc')
mmcaps = ('key','addr','rpc','tx')
base_coin = 'BTC'
addr_width = 34
addr_hex_width = 40
@staticmethod
def get_protocol_by_chain(chain):
@ -207,7 +206,7 @@ class BitcoinCashTestnetProtocol(BitcoinCashProtocol):
addr_width = 35
class B2XProtocol(BitcoinProtocol):
daemon_name = 'bitcoind-2x'
daemon_name = 'bitcoind-2x'
daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin_2X') if g.platform == 'win' \
else os.path.join(g.home_dir,'.bitcoin-2x')
rpc_port = 8338
@ -218,12 +217,12 @@ class B2XProtocol(BitcoinProtocol):
]
class B2XTestnetProtocol(B2XProtocol):
addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
wif_ver_num = { 'std': 'ef' }
data_subdir = 'testnet'
daemon_data_subdir = 'testnet5'
rpc_port = 18338
addr_width = 35
addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
wif_ver_num = { 'std': 'ef' }
data_subdir = 'testnet'
daemon_data_subdir = 'testnet5'
rpc_port = 18338
addr_width = 35
class LitecoinProtocol(BitcoinProtocol):
block0 = '12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2'
@ -250,9 +249,7 @@ class LitecoinTestnetProtocol(LitecoinProtocol):
addr_width = 35
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):
@ -299,9 +296,8 @@ class ZcashProtocol(BitcoinProtocolAddrgen):
'zcash_z': ('169a','zc'),
'viewkey': ('0b1c','V') }
wif_ver_num = { 'std': '80', 'zcash_z': 'ab36' }
mmtypes = ('C','Z')
dfl_mmtype = 'C'
addr_hex_width = 40
mmtypes = ('L','C','Z')
dfl_mmtype = 'L'
@classmethod
def preprocess_key(cls,hexpriv,pubkey_type): # zero the first four bits
@ -328,35 +324,22 @@ class ZcashTestnetProtocol(ZcashProtocol):
'zcash_z': ('16b6','??'),
'viewkey': ('0b2a','??') }
class DashProtocol(BitcoinProtocolAddrgen):
name = 'dash'
base_coin = 'DASH'
addr_ver_num = { 'p2pkh': ('4c','X'), 'p2sh': ('10','7') }
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 = { 'std': 'ef' }
class CoinProtocol(MMGenObject):
coins = {
'btc': (BitcoinProtocol,BitcoinTestnetProtocol),
'bch': (BitcoinCashProtocol,BitcoinCashTestnetProtocol),
'b2x': (B2XProtocol,B2XTestnetProtocol),
'ltc': (LitecoinProtocol,LitecoinTestnetProtocol),
'dash': (DashProtocol,DashTestnetProtocol),
'zec': (ZcashProtocol,ZcashTestnetProtocol),
'eth': (EthereumProtocol,EthereumTestnetProtocol),
'etc': (EthereumClassicProtocol,EthereumClassicTestnetProtocol),
'zec': (ZcashProtocol,ZcashTestnetProtocol),
}
def __new__(cls,coin,testnet):
coin = coin.lower()
assert type(testnet) == bool
m = "'{}': not a valid coin. Valid choices are '{}'"
assert coin in cls.coins,m.format(coin,"','".join(cls.coins))
from mmgen.altcoin import CoinInfo as ci
all_coins = sorted(set([e[1].lower() for e in ci.coin_constants['mainnet']] + cls.coins.keys()))
m = "'{}': not a valid coin. Valid choices are {}"
assert coin in cls.coins,m.format(coin,','.join(all_coins))
return cls.coins[coin][testnet]
@classmethod
@ -365,3 +348,60 @@ class CoinProtocol(MMGenObject):
if name == proto.__name__[:-8].lower():
return proto.base_coin
return False
def init_genonly_altcoins(usr_coin,trust_level=None):
from mmgen.altcoin import CoinInfo as ci
if trust_level is None:
if not usr_coin or usr_coin.lower() in CoinProtocol.coins: return None
usr_coin = usr_coin.upper()
mn_coins = [e[1] for e in ci.coin_constants['mainnet']]
if usr_coin not in mn_coins: return None
trust_level = ci.coin_constants['mainnet'][mn_coins.index(usr_coin)][6]
data = {}
for k in ('mainnet','testnet'):
data[k] = [e for e in ci.coin_constants[k] if e[6] >= trust_level]
exec(make_init_genonly_altcoins_str(data))
return trust_level
def make_init_genonly_altcoins_str(data):
def make_proto(e,testnet=False):
tn_str = 'Testnet' if testnet else ''
proto,coin = '{}{}Protocol'.format(e[0],tn_str),e[1]
if proto[0] in '0123456789': proto = 'X_'+proto
if proto in globals(): return ''
if coin.lower() in CoinProtocol.coins: return ''
def num2hexstr(n):
return '{:0{}x}'.format(n,2 if n < 256 else 4)
o = ['class {}(Bitcoin{}ProtocolAddrgen):'.format(proto,tn_str)]
o += ["base_coin = '{}'".format(coin)]
o += ["name = '{}'".format(e[0].lower())]
o += ["nameCaps = '{}'".format(e[0])]
a = "addr_ver_num = {{ 'p2pkh': ({!r},{!r})".format(num2hexstr(e[3][0]),e[3][1])
b = ", 'p2sh': ({!r},{!r})".format(num2hexstr(e[4][0]),e[4][1]) if e[4] else ''
o += [a+b+' }']
o += ["wif_ver_num = {{ 'std': {!r} }}".format(num2hexstr(e[2]))]
o += ["mmtypes = ('L','C'{})".format(",'S'" if e[5] else '')]
o += ["dfl_mmtype = '{}'".format('L')]
return '\n\t'.join(o) + '\n'
out = ''
for e in data['mainnet']:
out += make_proto(e)
for e in data['testnet']:
out += make_proto(e,testnet=True)
tn_coins = [e[1] for e in data['testnet']]
fs = "CoinProtocol.coins['{}'] = ({}Protocol,{})\n"
for e in data['mainnet']:
proto,coin = e[0],e[1]
if proto[0] in '0123456789': proto = 'X_'+proto
if proto+'Protocol' in globals(): continue
if coin.lower() in CoinProtocol.coins: continue
out += fs.format(coin.lower(),proto,('None',proto+'TestnetProtocol')[coin in tn_coins])
# print out
return out
def init_coin(coin):
g.coin = coin
g.proto = CoinProtocol(coin,g.testnet)

View file

@ -225,8 +225,8 @@ def print_convert_results(indata,enc,dec,dtype):
from mmgen.obj import MMGenAddrType
at = MMGenAddrType((hasattr(opt,'type') and opt.type) or g.proto.dfl_mmtype)
kg = KeyGenerator(at.pubkey_type)
ag = AddrGenerator(at.gen_method)
kg = KeyGenerator(at)
ag = AddrGenerator(at)
def Hexdump(infile,cols=8,line_nums=True):
Msg(pretty_hexdump(

View file

@ -266,7 +266,7 @@ class MMGenTX(MMGenObject):
desc = 'transaction outputs'
member_type = 'MMGenTxOutput'
def __init__(self,filename=None,md_only=False,caller=None):
def __init__(self,filename=None,md_only=False,caller=None,silent_open=False):
self.inputs = self.MMGenTxInputList()
self.outputs = self.MMGenTxOutputList()
self.send_amt = g.proto.coin_amt('0') # total amt minus change
@ -285,7 +285,7 @@ class MMGenTX(MMGenObject):
self.locktime = None
if filename:
self.parse_tx_file(filename,md_only=md_only)
self.parse_tx_file(filename,md_only=md_only,silent_open=silent_open)
if md_only: return
self.check_sigs() # marks the tx as signed
@ -727,22 +727,42 @@ class MMGenTX(MMGenObject):
ret = g.rpch.gettransaction(self.coin_txid,on_fail='silent')
if not 'bip125-replaceable' in ret or not 'confirmations' in ret or ret['confirmations'] > 0:
return False
return -ret['confirmations'] + 1 # 1: replacement in mempool, 2: replacement confirmed
return -ret['confirmations'] + 1,ret # 1: replacement in mempool, 2: replacement confirmed
def is_in_utxos(self):
return 'txid' in g.rpch.getrawtransaction(self.coin_txid,True,on_fail='silent')
def get_status(self,status=False):
if self.is_in_mempool():
msg(('Warning: transaction is in mempool!','Transaction is in mempool')[status])
if status:
d = g.rpch.gettransaction(self.coin_txid,on_fail='silent')
r = '{}replaceable'.format(('NOT ','')[d['bip125-replaceable']=='yes'])
t = d['timereceived']
m = 'Sent {} ({} h/m/s ago)'
b = m.format(time.strftime('%c',time.gmtime(t)),secs_to_dhms(int(time.time()-t)))
if opt.quiet:
msg('Transaction is in mempool')
else:
msg('TX status: in mempool, {}\n{}'.format(r,b))
else:
msg('Warning: transaction is in mempool!')
elif self.is_in_wallet():
confs = self.is_in_wallet()
die(0,'Transaction has {} confirmation{}'.format(confs,suf(confs,'s')))
elif self.is_in_utxos():
die(2,red('ERROR: transaction is in the blockchain (but not in the tracking wallet)!'))
ret = self.is_replaced() # 1: replacement in mempool, 2: replacement confirmed
if ret:
die(1,'Transaction has been replaced'+('',', and the replacement TX is confirmed')[ret==2]+'!')
else:
ret = self.is_replaced() # ret[0]==1: replacement in mempool, ret[0]==2: replacement confirmed
if ret and ret[0]:
m1 = 'Transaction has been replaced'
m2 = ('',', and the replacement TX is confirmed')[ret[0]==2]
msg('{}{}!'.format(m1,m2))
if not opt.quiet:
msg('Replacing transactions:')
rt = ret[1]['walletconflicts']
for t,s in [(tx,'size' in g.rpch.getmempoolentry(tx,on_fail='silent')) for tx in rt]:
msg(' {}{}'.format(t,('',' in mempool')[s]))
die(0,'')
def send(self,prompt_user=True,exit_on_fail=False):
@ -946,9 +966,9 @@ class MMGenTX(MMGenObject):
return out # TX label might contain non-ascii chars
def parse_tx_file(self,infile,md_only=False):
def parse_tx_file(self,infile,md_only=False,silent_open=False):
tx_data = get_lines_from_file(infile,self.desc+' data')
tx_data = get_lines_from_file(infile,self.desc+' data',silent=silent_open)
try:
desc = 'data'

View file

@ -212,6 +212,12 @@ def make_timestr(secs=None):
tv = time.gmtime(t)[:6]
return '{:04d}/{:02d}/{:02d} {:02d}:{:02d}:{:02d}'.format(*tv)
def secs_to_dhms(secs):
dsecs = secs/3600
return '{}{:02d}:{:02d}:{:02d}'.format(
('','{} day{}, '.format(dsecs/24,suf(dsecs/24)))[dsecs > 24],
dsecs % 24, (secs/60) % 60, secs % 60)
def secs_to_hms(secs):
return '{:02d}:{:02d}:{:02d}'.format(secs/3600, (secs/60) % 60, secs % 60)

View file

@ -100,6 +100,7 @@ f_obj='Data object test complete'
i_alts='Gen-only altcoin'
s_alts='The following tests will test generation operations for all supported altcoins'
ROUNDS=100
ROUNDS_LOW=20
ROUNDS_SPEC=500
t_alts=(
'test/scrambletest.py'
@ -110,7 +111,6 @@ t_alts=(
"test/gentest.py --coin=ltc 2 $ROUNDS"
"test/gentest.py --coin=ltc --type=compressed 2 $ROUNDS"
"test/gentest.py --coin=ltc --type=segwit 2 $ROUNDS"
"test/gentest.py --coin=dash 2 $ROUNDS"
"test/gentest.py --coin=zec 2 $ROUNDS"
"test/gentest.py --coin=etc 2 $ROUNDS"
"test/gentest.py --coin=eth 2 $ROUNDS"
@ -122,11 +122,15 @@ t_alts=(
"test/gentest.py --coin=ltc 2:ext $ROUNDS"
"test/gentest.py --coin=ltc --type=compressed 2:ext $ROUNDS"
# "test/gentest.py --coin=ltc --type=segwit 2:ext $ROUNDS" # pycoin generates old-style LTC Segwit addrs
"test/gentest.py --coin=dash 2:ext $ROUNDS"
"test/gentest.py --coin=zec 2:ext $ROUNDS"
"test/gentest.py --coin=etc 2:ext $ROUNDS"
"test/gentest.py --coin=eth 2:ext $ROUNDS"
"test/gentest.py --coin=zec --type=zcash_z 2:ext $ROUNDS_SPEC")
"test/gentest.py --coin=zec 2:ext $ROUNDS"
"test/gentest.py --coin=zec --type=zcash_z 2:ext $ROUNDS_SPEC"
"test/gentest.py --all 2:pycoin $ROUNDS_LOW"
"test/gentest.py --all 2:pyethereum $ROUNDS_LOW"
"test/gentest.py --all 2:keyconv $ROUNDS_LOW")
f_alts='Gen-only altcoin tests completed'
i_misc_ni='Miscellaneous operations (non-interactive)'
@ -164,7 +168,8 @@ i_btc_rt='Bitcoin regtest'
s_btc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode"
t_btc_rt=(
'test/test.py -On regtest'
'test/test.py -On regtest_split')
# 'test/test.py -On regtest_split' # no official B2X support, so skip
)
f_btc_rt='Regtest (Bob and Alice) mode tests for BTC completed'
i_bch='Bitcoin cash (BCH)'
@ -211,7 +216,6 @@ s_ltc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode"
t_ltc_rt=('test/test.py --coin=ltc -On regtest')
f_ltc_rt='Regtest (Bob and Alice) mode tests for LTC completed'
# TODO: ethereum support for tooltest
i_tool='Tooltest'
s_tool='The following tests will run test/tooltest.py for all supported coins'
t_tool=(
@ -222,6 +226,8 @@ t_tool=(
'test/tooltest.py --coin=eth cryptocoin'
'test/tooltest.py --coin=etc cryptocoin'
'test/tooltest.py --coin=dash cryptocoin'
'test/tooltest.py --coin=doge cryptocoin'
'test/tooltest.py --coin=emc cryptocoin'
'test/tooltest.py --coin=zec cryptocoin'
'test/tooltest.py --coin=zec --type=zcash_z cryptocoin')
f_tool='tooltest tests completed'

View file

@ -3,6 +3,7 @@ import sys,traceback,os
sys.path.insert(0,'.')
if 'TMUX' in os.environ: del os.environ['TMUX']
os.environ['MMGEN_TRACEBACK'] = '1'
f = open('my.err','w')

View file

@ -39,16 +39,14 @@ if g.coin != 'BTC':
if len(cmd_args) != 1: opts.usage()
from mmgen.protocol import CoinProtocol
import mmgen.tx
tx = mmgen.tx.MMGenTX(cmd_args[0])
if opt.verbose:
gmsg('Original transaction is in {} format'.format(g.coin))
g.coin = 'BCH'
g.proto = CoinProtocol(g.coin,g.testnet)
from mmgen.protocol import init_coin
init_coin('BCH')
reload(sys.modules['mmgen.tx'])

View file

@ -109,6 +109,7 @@ setup(
py_modules = [
'mmgen.__init__',
'mmgen.addr',
'mmgen.altcoin',
'mmgen.protocol',
'mmgen.color',
'mmgen.common',

View file

@ -24,6 +24,7 @@ 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))
os.environ['MMGEN_TEST_SUITE'] = '1'
from binascii import hexlify
@ -33,10 +34,11 @@ from mmgen.obj import MMGenAddrType
rounds = 100
opts_data = lambda: {
'desc': "Test address generation in various ways",
'desc': 'Test address generation in various ways',
'usage':'[options] [spec] [rounds | dump file]',
'options': """
-h, --help Print this help message
-a, --all Test all supported coins for external generator 'ext'
--, --longhelp Print help message for long options (common options)
-q, --quiet Produce quieter output
-t, --type=t Specify address type (valid options: 'compressed','segwit','zcash_z')
@ -62,8 +64,11 @@ EXAMPLES:
(compare addrs generated with secp256k1 library to {dn} wallet dump)
External libraries required for the 'ext' generator:
+ pyethereum (for ETH,ETC) https://github.com/ethereum/pyethereum
+ pycoin (for all other coins) https://github.com/richardkiss/pycoin
+ pyethereum (for ETH,ETC) https://github.com/ethereum/pyethereum
+ zcash-mini (for zcash_z addresses) https://github.com/FiloSottile/zcash-mini
+ pycoin (for supported coins) https://github.com/richardkiss/pycoin
+ keyconv (for all other coins) https://github.com/exploitagency/vanitygen-plus
('keyconv' generates uncompressed addresses only)
""".format(prog='gentest.py',pnm=g.proj_name,snum=rounds,dn=g.proto.daemon_name)
}
@ -78,6 +83,12 @@ addr_type = MMGenAddrType(opt.type or g.proto.dfl_mmtype)
def pyethereum_sec2addr(sec):
return sec,eth.privtoaddr(sec).encode('hex')
def keyconv_sec2addr(sec):
p = sp.Popen(['keyconv','-C',g.coin,sec.wif],stderr=sp.PIPE,stdout=sp.PIPE)
o = p.stdout.read().splitlines()
# print p.stderr.read()
return o[1].split()[1],o[0].split()[1]
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')
@ -85,41 +96,52 @@ def zcash_mini_sec2addr(sec):
return sec.wif,o[0],o[-1]
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]
else:
coin = g.coin
coin = ci.external_tests['testnet']['pycoin'][g.coin] if g.testnet else g.coin
key = pcku.parse_key(sec,PREFIX_TRANSFORMS,coin)
if key is None: die(1,"can't parse {}".format(sec))
o = pcku.create_output(sec,key)[0]
# pmsg(o)
suf = ('_uncompressed','')[addr_type.compressed]
wif = o['wif{}'.format(suf)]
addr = o['p2sh_segwit' if addr_type.name == 'segwit' else '{}_address{}'.format(coin,suf)]
return wif,addr
# pycoin/networks/all.py pycoin/networks/legacy_networks.py
def init_external_prog():
global b_desc,ext_lib,ext_sec2addr,sp,eth,pcku,PREFIX_TRANSFORMS
if addr_type.name == 'zcash_z':
global b,b_desc,ext_lib,ext_sec2addr,sp,eth,pcku,PREFIX_TRANSFORMS
def test_support(k):
if b == k: return True
if b != 'ext' and b != k: return False
if g.coin in ci.external_tests['mainnet'][k] and not g.testnet: return True
if g.coin in ci.external_tests['testnet'][k]: return True
return False
if b == 'zcash_mini' or addr_type.name == 'zcash_z':
import subprocess as sp
ext_sec2addr = zcash_mini_sec2addr
ext_lib = 'zcash_mini'
elif addr_type.name == 'ethereum':
elif test_support('pyethereum'):
try:
import ethereum.utils as eth
except:
die(1,"Unable to import 'pyethereum' module. Is pyethereum installed?")
raise ImportError,"Unable to import 'pyethereum' module. Is pyethereum installed?"
ext_sec2addr = pyethereum_sec2addr
ext_lib = 'pyethereum'
else:
elif test_support('pycoin'):
try:
import pycoin.cmds.ku as pcku
except:
die(1,"Unable to import module 'ku'. Is pycoin installed?")
raise ImportError,"Unable to import module 'ku'. Is pycoin installed?"
PREFIX_TRANSFORMS = pcku.prefix_transforms_for_network(g.coin)
ext_sec2addr = pycoin_sec2addr
ext_lib = 'pycoin'
elif test_support('keyconv'):
import subprocess as sp
ext_sec2addr = keyconv_sec2addr
ext_lib = 'keyconv'
else:
m = '{}: coin supported by MMGen but unsupported by gentest.py for {}'
raise ValueError,m.format(g.coin,('mainnet','testnet')[g.testnet])
b_desc = ext_lib
b = 'ext'
def match_error(sec,wif,a_addr,b_addr,a,b):
qmsg_r(red('\nERROR: Values do not match!'))
@ -131,6 +153,14 @@ def match_error(sec,wif,a_addr,b_addr,a,b):
""".format(sec,wif,a_addr,b_addr,pnm=g.proj_name,a=kg_a.desc,b=b_desc).rstrip())
def compare_test():
for k in ('segwit','compressed'):
if addr_type.name == k and g.coin not in ci.external_tests_segwit_compressed[k]:
msg('{} testing not supported for coin {}'.format(addr_type.name.capitalize(),g.coin))
return
if 'ext_lib' in globals():
if g.coin not in ci.external_tests[('mainnet','testnet')[g.testnet]][ext_lib]:
msg("Coin '{}' incompatible with external generator '{}'".format(g.coin,ext_lib))
return
m = "Comparing address generators '{}' and '{}' for coin {}"
last_t = time.time()
qmsg(green(m.format(kg_a.desc,(ext_lib if b == 'ext' else kg_b.desc),g.coin)))
@ -142,10 +172,10 @@ def compare_test():
sec = PrivKey(os.urandom(32),compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
ph = kg_a.to_pubhex(sec)
a_addr = ag.to_addr(ph)
if opt.type == 'zcash_z':
if addr_type.name == 'zcash_z':
a_vk = ag.to_viewkey(ph)
if b == 'ext':
if opt.type == 'zcash_z':
if addr_type.name == 'zcash_z':
b_wif,b_addr,b_vk = ext_sec2addr(sec)
if b_vk != a_vk:
match_error(sec,sec.wif,a_vk,b_vk,a,b)
@ -195,6 +225,7 @@ def dump_test():
match_error(sec,wif,a_addr,b_addr,3,a)
qmsg(green(('\n','')[bool(opt.verbose)] + 'OK'))
from mmgen.altcoin import CoinInfo as ci
urounds,fh = None,None
dump = []
if len(cmd_args) == 2:
@ -205,7 +236,7 @@ if len(cmd_args) == 2:
try:
fh = open(cmd_args[1])
except:
die(1,"Second argument must be filename or positive integer")
die(1,'Second argument must be filename or positive integer')
else:
for line in fh.readlines():
if 'addr=' in line:
@ -224,31 +255,43 @@ except:
a = int(a)
assert 1 <= a <= len(g.key_generators)
except:
die(1,"First argument must be one or two generator IDs, colon separated")
die(1,'First argument must be one or two generator IDs, colon separated')
else:
try:
a = int(a)
assert 1 <= a <= len(g.key_generators)
if b == 'ext':
assert 1 <= a <= len(g.key_generators),'{}: invalid key generator'.format(a)
if b in ('ext','pyethereum','pycoin','keyconv','zcash_mini'):
init_external_prog()
else:
b = int(b)
assert 1 <= b <= len(g.key_generators)
assert a != b
except:
die(1,"%s: invalid generator IDs" % cmd_args[0])
assert 1 <= b <= len(g.key_generators),'{}: invalid key generator'.format(b)
assert a != b,'Key generators are the same!'
except Exception as e:
die(1,'{}\n{}: invalid generator argument'.format(e[0],cmd_args[0]))
from mmgen.addr import KeyGenerator,AddrGenerator
from mmgen.obj import PrivKey
kg_a = KeyGenerator(addr_type.pubkey_type,a)
ag = AddrGenerator(addr_type.gen_method)
kg_a = KeyGenerator(addr_type,a)
ag = AddrGenerator(addr_type)
if a and b:
if b != 'ext':
kg_b = KeyGenerator(addr_type.pubkey_type,b)
b_desc = kg_b.desc
compare_test()
if opt.all:
from mmgen.protocol import init_coin,init_genonly_altcoins
init_genonly_altcoins('btc',trust_level=0)
mmgen_supported = [e[1] for e in ci.coin_constants['mainnet']]
for coin in ci.external_tests[('mainnet','testnet')[g.testnet]][ext_lib]:
if coin not in mmgen_supported and ext_lib != 'pyethereum': continue
init_coin(coin)
tmp_addr_type = addr_type if addr_type in g.proto.mmtypes else MMGenAddrType(g.proto.dfl_mmtype)
kg_a = KeyGenerator(tmp_addr_type,a)
ag = AddrGenerator(tmp_addr_type)
compare_test()
else:
if b != 'ext':
kg_b = KeyGenerator(addr_type,b)
b_desc = kg_b.desc
compare_test()
elif a and not fh:
speed_test()
elif a and dump:

View file

@ -24,6 +24,7 @@ import sys,os,subprocess
repo_root = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.pardir)))
os.chdir(repo_root)
sys.path.__setitem__(0,repo_root)
os.environ['MMGEN_TEST_SUITE'] = '1'
# Import this _after_ local path's been added to sys.path
from mmgen.common import *
@ -60,13 +61,14 @@ test_data = OrderedDict([
('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')),
('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')),
('dash', ('1319d347b021f952','dash:legacy', '-DASH','DASH','XoK491fppGNZQUUS9uEFkT6L9u8xxVFJNJ')),
('zec', ('0bf9b5b20af7b5a0','zec:legacy', '-ZEC', 'ZEC', 't1URz8BHxV38v3gsaN6oHQNKC16s35R9WkY')),
('zec_zcash_z', ('b15570d033df9b1a','zec:zcash_z', '-ZEC-Z','ZEC:ZCASH_Z','zcLMMsnzfFYZWU4avRBnuc83yh4jTtJXbtP32uWrs3ickzu1krMU4ppZCQPTwwfE9hLnRuFDSYF8VFW13aT9eeQK8aov3Ge')),
('emc', ('7e1a29853d2db875','emc:legacy', '-EMC', 'EMC','EU4L6x2b5QUb2gRQsBAAuB8TuPEwUxCNZU')),
])
def run_tests():

View file

@ -791,11 +791,12 @@ for a,b in cmd_group['regtest']:
cmd_list['regtest'].append(a)
cmd_data[a] = (17,b,[[[],17]])
cmd_data['info_regtest_split'] = 'regtest mode with fork and coin split',[17]
for a,b in cmd_group['regtest_split']:
cmd_list['regtest_split'].append(a)
cmd_data[a] = (19,b,[[[],19]])
# disable until B2X officially supported
# cmd_data['info_regtest_split'] = 'regtest mode with fork and coin split',[17]
# for a,b in cmd_group['regtest_split']:
# cmd_list['regtest_split'].append(a)
# cmd_data[a] = (19,b,[[[],19]])
#
cmd_data['info_misc'] = 'miscellaneous operations',[18]
for a,b in cmd_group['misc']:
cmd_list['misc'].append(a)

View file

@ -24,6 +24,7 @@ import sys,os,subprocess
repo_root = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.pardir)))
os.chdir(repo_root)
sys.path.__setitem__(0,repo_root)
os.environ['MMGEN_TEST_SUITE'] = '1'
# Import this _after_ local path's been added to sys.path
from mmgen.common import *