diff --git a/README.md b/README.md index 5c94a0c5..22733ced 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/cmds/mmgen-autosign b/cmds/mmgen-autosign index be043fa5..ecfb2b82 100755 --- a/cmds/mmgen-autosign +++ b/cmds/mmgen-autosign @@ -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) diff --git a/mmgen/addr.py b/mmgen/addr.py index f3b31e1b..34d82719 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -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)) diff --git a/mmgen/altcoin.py b/mmgen/altcoin.py new file mode 100755 index 00000000..70663ae5 --- /dev/null +++ b/mmgen/altcoin.py @@ -0,0 +1,529 @@ +#!/usr/bin/env python +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2017 Philemon +# +# 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 . + +""" +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'), + } diff --git a/mmgen/main.py b/mmgen/main.py index 1aaac2d7..3ba414eb 100755 --- a/mmgen/main.py +++ b/mmgen/main.py @@ -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) diff --git a/mmgen/main_split.py b/mmgen/main_split.py index c3b6556a..0c27a17c 100755 --- a/mmgen/main_split.py +++ b/mmgen/main_split.py @@ -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() diff --git a/mmgen/main_tool.py b/mmgen/main_tool.py index f09e4cd7..632b4bf3 100755 --- a/mmgen/main_tool.py +++ b/mmgen/main_tool.py @@ -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 diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index 8bfe4d15..9cf76d40 100755 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -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(): diff --git a/mmgen/opts.py b/mmgen/opts.py index ae638c14..1093ac04 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -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): diff --git a/mmgen/protocol.py b/mmgen/protocol.py index a41f272c..83ba6684 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -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) diff --git a/mmgen/tool.py b/mmgen/tool.py index fb90f6f4..a0f7ca31 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -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( diff --git a/mmgen/tx.py b/mmgen/tx.py index fbfe7147..56f731ac 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -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' diff --git a/mmgen/util.py b/mmgen/util.py index ac47a077..c1c08a3d 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -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) diff --git a/scripts/test-release.sh b/scripts/test-release.sh index 162e0b6f..9b70f806 100755 --- a/scripts/test-release.sh +++ b/scripts/test-release.sh @@ -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' diff --git a/scripts/traceback.py b/scripts/traceback.py index 550bbf91..8860ce5d 100755 --- a/scripts/traceback.py +++ b/scripts/traceback.py @@ -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') diff --git a/scripts/tx-btc2bch.py b/scripts/tx-btc2bch.py index 14af4b96..5616b6d2 100755 --- a/scripts/tx-btc2bch.py +++ b/scripts/tx-btc2bch.py @@ -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']) diff --git a/setup.py b/setup.py index 17fb9b7c..221d3b25 100755 --- a/setup.py +++ b/setup.py @@ -109,6 +109,7 @@ setup( py_modules = [ 'mmgen.__init__', 'mmgen.addr', + 'mmgen.altcoin', 'mmgen.protocol', 'mmgen.color', 'mmgen.common', diff --git a/test/gentest.py b/test/gentest.py index 5c7efa01..bb503b46 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -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: diff --git a/test/scrambletest.py b/test/scrambletest.py index 3d733de8..7acb3b91 100755 --- a/test/scrambletest.py +++ b/test/scrambletest.py @@ -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(): diff --git a/test/test.py b/test/test.py index 36e5ae3e..bdcec197 100755 --- a/test/test.py +++ b/test/test.py @@ -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) diff --git a/test/tooltest.py b/test/tooltest.py index 067e7d74..a69ba4e1 100755 --- a/test/tooltest.py +++ b/test/tooltest.py @@ -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 *