protocol.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. #!/usr/bin/env python
  2. #
  3. # MMGen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2018 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. protocol.py: Coin protocol functions, classes and methods
  20. """
  21. import sys,os,hashlib
  22. from binascii import unhexlify
  23. from mmgen.util import msg,pmsg,ymsg,Msg,pdie,ydie
  24. from mmgen.obj import MMGenObject,BTCAmt,LTCAmt,BCHAmt,B2XAmt,ETHAmt
  25. from mmgen.globalvars import g
  26. import mmgen.bech32 as bech32
  27. def hash160(hexnum): # take hex, return hex - OP_HASH160
  28. return hashlib.new('ripemd160',hashlib.sha256(unhexlify(hexnum)).digest()).hexdigest()
  29. def hash256(hexnum): # take hex, return hex - OP_HASH256
  30. return hashlib.sha256(hashlib.sha256(unhexlify(hexnum)).digest()).hexdigest()
  31. # From en.bitcoin.it:
  32. # The Base58 encoding used is home made, and has some differences.
  33. # Especially, leading zeroes are kept as single zeroes when conversion happens.
  34. # Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
  35. # The 'zero address':
  36. # 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20)
  37. _b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
  38. def _numtob58(num):
  39. def b58enc(n):
  40. while n:
  41. yield _b58a[n % 58]
  42. n /= 58
  43. return ''.join(b58enc(num))[::-1]
  44. def _b58tonum(b58str):
  45. return sum(_b58a.index(ch) * 58**n for n,ch in enumerate(b58str[::-1]))
  46. def _b58chk_encode(hexstr):
  47. return _numtob58(int(hexstr+hash256(hexstr)[:8],16))
  48. def _b58chk_decode(s):
  49. hexstr = '{:x}'.format(_b58tonum(s))
  50. if len(hexstr) % 2: hexstr = '0' + hexstr
  51. if hexstr[-8:] == hash256(hexstr[:-8])[:8]:
  52. return hexstr[:-8]
  53. raise ValueError,'_b58chk_decode(): checksum incorrect'
  54. # chainparams.cpp
  55. class BitcoinProtocol(MMGenObject):
  56. name = 'bitcoin'
  57. daemon_name = 'bitcoind'
  58. daemon_family = 'bitcoind'
  59. addr_ver_num = { 'p2pkh': ('00','1'), 'p2sh': ('05','3') }
  60. wif_ver_num = { 'std': '80' }
  61. mmtypes = ('L','C','S','B')
  62. dfl_mmtype = 'L'
  63. data_subdir = ''
  64. rpc_port = 8332
  65. secs_per_block = 600
  66. coin_amt = BTCAmt
  67. max_tx_fee = BTCAmt('0.003')
  68. daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin') if g.platform == 'win' \
  69. else os.path.join(g.home_dir,'.bitcoin')
  70. daemon_data_subdir = ''
  71. sighash_type = 'ALL'
  72. block0 = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
  73. forks = [ # height, hash, name, replayable
  74. (478559,'00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148','bch',False),
  75. (None,'','b2x',True)
  76. ]
  77. caps = ('rbf','segwit')
  78. mmcaps = ('key','addr','rpc','tx')
  79. base_coin = 'BTC'
  80. # From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00,
  81. # but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal).
  82. witness_vernum_hex = '00'
  83. witness_vernum = int(witness_vernum_hex,16)
  84. bech32_hrp = 'bc'
  85. sign_mode = 'daemon'
  86. secp256k1_ge = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
  87. @classmethod
  88. def is_testnet(cls):
  89. return cls.__name__[-15:] == 'TestnetProtocol'
  90. @staticmethod
  91. def get_protocol_by_chain(chain):
  92. return CoinProtocol(g.coin,{'mainnet':False,'testnet':True,'regtest':True}[chain])
  93. @classmethod
  94. def cap(cls,s): return s in cls.caps
  95. @classmethod
  96. def preprocess_key(cls,hexpriv,pubkey_type):
  97. # Key must be non-zero and less than group order of secp256k1 curve
  98. if 0 < int(hexpriv,16) < cls.secp256k1_ge:
  99. return hexpriv
  100. else: # chance of this is less than 1 in 2^127
  101. pk = int(hexpriv,16)
  102. if pk == 0: # chance of this is 1 in 2^256
  103. ydie(3,'Private key is zero!')
  104. elif pk == cls.secp256k1_ge: # ditto
  105. ydie(3,'Private key == secp256k1_ge!')
  106. else:
  107. ymsg('Warning: private key is greater than secp256k1 group order!:\n {}'.format(hexpriv))
  108. return '{:064x}'.format(pk % cls.secp256k1_ge)
  109. @classmethod
  110. def hex2wif(cls,hexpriv,pubkey_type,compressed):
  111. return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + ('','01')[bool(compressed)])
  112. @classmethod
  113. def wif2hex(cls,wif):
  114. key = _b58chk_decode(wif)
  115. pubkey_type = None
  116. for k,v in cls.wif_ver_num.items():
  117. if key[:len(v)] == v:
  118. pubkey_type = k
  119. key = key[len(v):]
  120. assert pubkey_type,'invalid WIF version number'
  121. if len(key) == 66:
  122. assert key[-2:] == '01','invalid compressed key suffix'
  123. compressed = True
  124. else:
  125. assert len(key) == 64,'invalid key length'
  126. compressed = False
  127. assert 0 < int(key[:64],16) < cls.secp256k1_ge,(
  128. "'{}': invalid WIF (produces key outside allowable range)".format(wif))
  129. return { 'hex':key[:64], 'pubkey_type':pubkey_type, 'compressed':compressed }
  130. @classmethod
  131. def verify_addr(cls,addr,hex_width,return_dict=False):
  132. if 'B' in cls.mmtypes and addr[:len(cls.bech32_hrp)] == cls.bech32_hrp:
  133. ret = bech32.decode(cls.bech32_hrp,addr)
  134. if ret[0] != cls.witness_vernum:
  135. msg('{}: Invalid witness version number'.format(ret[0]))
  136. elif ret[1]:
  137. return {
  138. 'hex': ''.join(map(chr,ret[1])).encode('hex'),
  139. 'format': 'bech32'
  140. } if return_dict else True
  141. return False
  142. for addr_fmt in cls.addr_ver_num:
  143. ver_num,pfx = cls.addr_ver_num[addr_fmt]
  144. if type(pfx) == tuple:
  145. if addr[0] not in pfx: continue
  146. elif addr[:len(pfx)] != pfx: continue
  147. num = _b58tonum(addr)
  148. if num == False:
  149. if g.debug: Msg('Address cannot be converted to base 58')
  150. break
  151. addr_hex = '{:0{}x}'.format(num,len(ver_num)+hex_width+8)
  152. # pmsg(hex_width,len(addr_hex),addr_hex[:len(ver_num)],ver_num)
  153. if addr_hex[:len(ver_num)] != ver_num: continue
  154. if hash256(addr_hex[:-8])[:8] == addr_hex[-8:]:
  155. return {
  156. 'hex': addr_hex[len(ver_num):-8],
  157. 'format': {'p2pkh':'p2pkh','p2sh':'p2sh','p2sh2':'p2sh',
  158. 'zcash_z':'zcash_z','viewkey':'viewkey'}[addr_fmt]
  159. } if return_dict else True
  160. else:
  161. if g.debug: Msg('Invalid checksum in address')
  162. break
  163. return False
  164. @classmethod
  165. def pubhash2addr(cls,pubkey_hash,p2sh):
  166. assert len(pubkey_hash) == 40,'{}: invalid length for pubkey hash'.format(len(pubkey_hash))
  167. s = cls.addr_ver_num[('p2pkh','p2sh')[p2sh]][0] + pubkey_hash
  168. lzeroes = (len(s) - len(s.lstrip('0'))) / 2 # non-zero only for ver num '00' (BTC p2pkh)
  169. return ('1' * lzeroes) + _b58chk_encode(s)
  170. # Segwit:
  171. @classmethod
  172. def pubhex2redeem_script(cls,pubhex):
  173. # https://bitcoincore.org/en/segwit_wallet_dev/
  174. # The P2SH redeemScript is always 22 bytes. It starts with a OP_0, followed
  175. # by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash})
  176. return cls.witness_vernum_hex + '14' + hash160(pubhex)
  177. @classmethod
  178. def pubhex2segwitaddr(cls,pubhex):
  179. return cls.pubhash2addr(hash160(cls.pubhex2redeem_script(pubhex)),p2sh=True)
  180. @classmethod
  181. def pubhash2bech32addr(cls,pubhash):
  182. d = map(ord,pubhash.decode('hex'))
  183. return bech32.bech32_encode(cls.bech32_hrp,[cls.witness_vernum]+bech32.convertbits(d,8,5))
  184. class BitcoinTestnetProtocol(BitcoinProtocol):
  185. addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
  186. wif_ver_num = { 'std': 'ef' }
  187. data_subdir = 'testnet'
  188. daemon_data_subdir = 'testnet3'
  189. rpc_port = 18332
  190. bech32_hrp = 'tb'
  191. bech32_hrp_rt = 'bcrt'
  192. class BitcoinCashProtocol(BitcoinProtocol):
  193. # TODO: assumes MSWin user installs in custom dir 'Bitcoin_ABC'
  194. daemon_name = 'bitcoind-abc'
  195. daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin_ABC') if g.platform == 'win' \
  196. else os.path.join(g.home_dir,'.bitcoin-abc')
  197. rpc_port = 8442
  198. mmtypes = ('L','C')
  199. sighash_type = 'ALL|FORKID'
  200. forks = [
  201. (478559,'000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec','btc',False)
  202. ]
  203. caps = ()
  204. coin_amt = BCHAmt
  205. max_tx_fee = BCHAmt('0.1')
  206. @classmethod
  207. def pubhex2redeem_script(cls,pubhex): raise NotImplementedError
  208. @classmethod
  209. def pubhex2segwitaddr(cls,pubhex): raise NotImplementedError
  210. class BitcoinCashTestnetProtocol(BitcoinCashProtocol):
  211. rpc_port = 18442
  212. addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
  213. wif_ver_num = { 'std': 'ef' }
  214. data_subdir = 'testnet'
  215. daemon_data_subdir = 'testnet3'
  216. class B2XProtocol(BitcoinProtocol):
  217. daemon_name = 'bitcoind-2x'
  218. daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin_2X') if g.platform == 'win' \
  219. else os.path.join(g.home_dir,'.bitcoin-2x')
  220. rpc_port = 8338
  221. coin_amt = B2XAmt
  222. max_tx_fee = B2XAmt('0.1')
  223. forks = [
  224. (None,'','btc',True) # activation: 494784
  225. ]
  226. class B2XTestnetProtocol(B2XProtocol):
  227. addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('c4','2') }
  228. wif_ver_num = { 'std': 'ef' }
  229. data_subdir = 'testnet'
  230. daemon_data_subdir = 'testnet5'
  231. rpc_port = 18338
  232. class LitecoinProtocol(BitcoinProtocol):
  233. block0 = '12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2'
  234. name = 'litecoin'
  235. daemon_name = 'litecoind'
  236. daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Litecoin') if g.platform == 'win' \
  237. else os.path.join(g.home_dir,'.litecoin')
  238. addr_ver_num = { 'p2pkh': ('30','L'), 'p2sh': ('32','M'), 'p2sh2': ('05','3') } # 'p2sh' is new fmt
  239. wif_ver_num = { 'std': 'b0' }
  240. mmtypes = ('L','C','S','B')
  241. secs_per_block = 150
  242. rpc_port = 9332
  243. coin_amt = LTCAmt
  244. max_tx_fee = LTCAmt('0.3')
  245. base_coin = 'LTC'
  246. forks = []
  247. bech32_hrp = 'ltc'
  248. class LitecoinTestnetProtocol(LitecoinProtocol):
  249. # addr ver nums same as Bitcoin testnet, except for 'p2sh'
  250. addr_ver_num = { 'p2pkh': ('6f',('m','n')), 'p2sh': ('3a','Q'), 'p2sh2': ('c4','2') }
  251. wif_ver_num = { 'std': 'ef' } # same as Bitcoin testnet
  252. data_subdir = 'testnet'
  253. daemon_data_subdir = 'testnet4'
  254. rpc_port = 19332
  255. bech32_hrp = 'tltc'
  256. bech32_hrp_rt = 'rltc'
  257. class BitcoinProtocolAddrgen(BitcoinProtocol): mmcaps = ('key','addr')
  258. class BitcoinTestnetProtocolAddrgen(BitcoinTestnetProtocol): mmcaps = ('key','addr')
  259. class DummyWIF(object):
  260. @classmethod
  261. def hex2wif(cls,hexpriv,pubkey_type,compressed):
  262. n = cls.name.capitalize()
  263. assert pubkey_type == cls.pubkey_type,'{}: invalid pubkey_type for {}!'.format(pubkey_type,n)
  264. assert compressed == False,'{} does not support compressed pubkeys!'.format(n)
  265. return str(hexpriv)
  266. @classmethod
  267. def wif2hex(cls,wif):
  268. return { 'hex':str(wif), 'pubkey_type':cls.pubkey_type, 'compressed':False }
  269. class EthereumProtocol(DummyWIF,BitcoinProtocol):
  270. addr_width = 40
  271. mmtypes = ('E',)
  272. dfl_mmtype = 'E'
  273. name = 'ethereum'
  274. base_coin = 'ETH'
  275. pubkey_type = 'std' # required by DummyWIF
  276. data_subdir = ''
  277. daemon_name = 'parity'
  278. daemon_family = 'parity'
  279. rpc_port = 8545
  280. mmcaps = ('key','addr','rpc')
  281. coin_amt = ETHAmt
  282. max_tx_fee = ETHAmt('0.005')
  283. chain_name = 'foundation'
  284. sign_mode = 'standalone'
  285. caps = ('token',)
  286. @classmethod
  287. def verify_addr(cls,addr,hex_width,return_dict=False):
  288. from mmgen.util import is_hex_str_lc
  289. if is_hex_str_lc(addr) and len(addr) == cls.addr_width:
  290. return { 'hex': addr, 'format': 'ethereum' } if return_dict else True
  291. if g.debug: Msg("Invalid address '{}'".format(addr))
  292. return False
  293. @classmethod
  294. def pubhash2addr(cls,pubkey_hash,p2sh):
  295. assert len(pubkey_hash) == 40,'{}: invalid length for pubkey hash'.format(len(pubkey_hash))
  296. assert not p2sh,'Ethereum has no P2SH address format'
  297. return pubkey_hash
  298. class EthereumTestnetProtocol(EthereumProtocol):
  299. data_subdir = 'testnet'
  300. rpc_port = 8547 # start Parity with --jsonrpc-port=8547 or --ports-shift=2
  301. chain_name = 'kovan'
  302. class EthereumClassicProtocol(EthereumProtocol):
  303. name = 'ethereumClassic'
  304. class_pfx = 'Ethereum'
  305. rpc_port = 8555 # start Parity with --jsonrpc-port=8555 or --ports-shift=10
  306. chain_name = 'ethereum_classic' # chain_id 0x3d (61)
  307. class EthereumClassicTestnetProtocol(EthereumClassicProtocol):
  308. rpc_port = 8557 # start Parity with --jsonrpc-port=8557 or --ports-shift=12
  309. chain_name = 'classic-testnet' # aka Morden, chain_id 0x3e (62) (UNTESTED)
  310. class ZcashProtocol(BitcoinProtocolAddrgen):
  311. name = 'zcash'
  312. base_coin = 'ZEC'
  313. addr_ver_num = {
  314. 'p2pkh': ('1cb8','t1'),
  315. 'p2sh': ('1cbd','t3'),
  316. 'zcash_z': ('169a','zc'),
  317. 'viewkey': ('a8abd3','ZiVK') }
  318. wif_ver_num = { 'std': '80', 'zcash_z': 'ab36' }
  319. mmtypes = ('L','C','Z')
  320. dfl_mmtype = 'L'
  321. @classmethod
  322. def preprocess_key(cls,hexpriv,pubkey_type): # zero the first four bits
  323. if pubkey_type == 'zcash_z':
  324. return '{:02x}'.format(int(hexpriv[:2],16) & 0x0f) + hexpriv[2:]
  325. else:
  326. return hexpriv
  327. @classmethod
  328. def pubhash2addr(cls,pubkey_hash,p2sh):
  329. hl = len(pubkey_hash)
  330. if hl == 40:
  331. return super(cls,cls).pubhash2addr(pubkey_hash,p2sh)
  332. elif hl == 128:
  333. raise NotImplementedError,'Zcash z-addresses have no pubkey hash'
  334. else:
  335. raise ValueError,'{}: incorrect pubkey_hash length'.format(hl)
  336. class ZcashTestnetProtocol(ZcashProtocol):
  337. wif_ver_num = { 'std': 'ef', 'zcash_z': 'ac08' }
  338. addr_ver_num = {
  339. 'p2pkh': ('1d25','tm'),
  340. 'p2sh': ('1cba','t2'),
  341. 'zcash_z': ('16b6','zt'),
  342. 'viewkey': ('a8ac0c','ZiVt') }
  343. # https://github.com/monero-project/monero/blob/master/src/cryptonote_config.h
  344. class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen):
  345. name = 'monero'
  346. base_coin = 'XMR'
  347. addr_ver_num = { 'monero': ('12','4'), 'monero_sub': ('2a','8') } # 18,42
  348. wif_ver_num = {}
  349. mmtypes = ('M',)
  350. dfl_mmtype = 'M'
  351. addr_width = 95
  352. pubkey_type = 'monero' # required by DummyWIF
  353. @classmethod
  354. def preprocess_key(cls,hexpriv,pubkey_type): # reduce key
  355. try:
  356. from ed25519ll.djbec import l
  357. except:
  358. from mmgen.ed25519 import l
  359. n = int(hexpriv.decode('hex')[::-1].encode('hex'),16) % l
  360. return '{:064x}'.format(n).decode('hex')[::-1].encode('hex')
  361. @classmethod
  362. def verify_addr(cls,addr,hex_width,return_dict=False):
  363. def b58dec(addr_str):
  364. from mmgen.util import baseconv
  365. dec,l = baseconv.tohex,len(addr_str)
  366. a = ''.join([dec(addr_str[i*11:i*11+11],'b58',pad=16) for i in range(l/11)])
  367. b = dec(addr_str[-(l%11):],'b58',pad=10)
  368. return a + b
  369. from mmgen.util import is_b58_str
  370. assert is_b58_str(addr),'Not valid base-58 string'
  371. assert len(addr) == cls.addr_width,'Incorrect width'
  372. ret = b58dec(addr)
  373. import sha3
  374. chk = sha3.keccak_256(ret.decode('hex')[:-4]).hexdigest()[:8]
  375. assert chk == ret[-8:],'Incorrect checksum'
  376. return { 'hex': ret, 'format': 'monero' } if return_dict else True
  377. class MoneroTestnetProtocol(MoneroProtocol):
  378. addr_ver_num = { 'monero': ('35','4'), 'monero_sub': ('3f','8') } # 53,63
  379. class CoinProtocol(MMGenObject):
  380. coins = {
  381. # mainnet testnet trustlevel (None == skip)
  382. 'btc': (BitcoinProtocol,BitcoinTestnetProtocol,None),
  383. 'bch': (BitcoinCashProtocol,BitcoinCashTestnetProtocol,None),
  384. 'ltc': (LitecoinProtocol,LitecoinTestnetProtocol,None),
  385. 'eth': (EthereumProtocol,EthereumTestnetProtocol,None),
  386. 'etc': (EthereumClassicProtocol,EthereumClassicTestnetProtocol,None),
  387. 'zec': (ZcashProtocol,ZcashTestnetProtocol,2),
  388. 'xmr': (MoneroProtocol,MoneroTestnetProtocol,None)
  389. }
  390. def __new__(cls,coin,testnet):
  391. coin = coin.lower()
  392. assert type(testnet) == bool
  393. m = "'{}': not a valid coin. Valid choices are {}"
  394. assert coin in cls.coins,m.format(coin,','.join(cls.get_valid_coins()))
  395. return cls.coins[coin][testnet]
  396. @classmethod
  397. def get_valid_coins(cls,upcase=False):
  398. from mmgen.altcoin import CoinInfo as ci
  399. ret = sorted(set(
  400. [e[1] for e in ci.coin_constants['mainnet'] if e[6] != -1]
  401. + cls.coins.keys()))
  402. return [getattr(e,('lower','upper')[upcase])() for e in ret]
  403. @classmethod
  404. def get_base_coin_from_name(cls,name):
  405. for proto,foo in cls.coins.values():
  406. if name == proto.__name__[:-8].lower():
  407. return proto.base_coin
  408. return False
  409. def init_genonly_altcoins(usr_coin,trust_level=None):
  410. from mmgen.altcoin import CoinInfo as ci
  411. if trust_level is None:
  412. if not usr_coin: return None # BTC
  413. if usr_coin.lower() in CoinProtocol.coins:
  414. return CoinProtocol.coins[usr_coin.lower()][2]
  415. usr_coin = usr_coin.upper()
  416. mn_coins = [e[1] for e in ci.coin_constants['mainnet'] if e[6] != -1]
  417. if usr_coin not in mn_coins: return None
  418. trust_level = ci.coin_constants['mainnet'][mn_coins.index(usr_coin)][6]
  419. data = {}
  420. for k in ('mainnet','testnet'):
  421. data[k] = [e for e in ci.coin_constants[k] if e[6] >= trust_level]
  422. exec(make_init_genonly_altcoins_str(data))
  423. return trust_level
  424. def make_init_genonly_altcoins_str(data):
  425. def make_proto(e,testnet=False):
  426. tn_str = 'Testnet' if testnet else ''
  427. proto,coin = '{}{}Protocol'.format(e[0],tn_str),e[1]
  428. if proto[0] in '0123456789': proto = 'X_'+proto
  429. if proto in globals(): return ''
  430. if coin.lower() in CoinProtocol.coins: return ''
  431. def num2hexstr(n):
  432. return '{:0{}x}'.format(n,2 if n < 256 else 4)
  433. o = ['class {}(Bitcoin{}ProtocolAddrgen):'.format(proto,tn_str)]
  434. o += ["base_coin = '{}'".format(coin)]
  435. o += ["name = '{}'".format(e[0].lower())]
  436. o += ["nameCaps = '{}'".format(e[0])]
  437. a = "addr_ver_num = {{ 'p2pkh': ({!r},{!r})".format(num2hexstr(e[3][0]),e[3][1])
  438. b = ", 'p2sh': ({!r},{!r})".format(num2hexstr(e[4][0]),e[4][1]) if e[4] else ''
  439. o += [a+b+' }']
  440. o += ["wif_ver_num = {{ 'std': {!r} }}".format(num2hexstr(e[2]))]
  441. o += ["mmtypes = ('L','C'{})".format(",'S'" if e[5] else '')]
  442. o += ["dfl_mmtype = '{}'".format('L')]
  443. return '\n\t'.join(o) + '\n'
  444. out = ''
  445. for e in data['mainnet']:
  446. out += make_proto(e)
  447. for e in data['testnet']:
  448. out += make_proto(e,testnet=True)
  449. tn_coins = [e[1] for e in data['testnet']]
  450. fs = "CoinProtocol.coins['{}'] = ({}Protocol,{})\n"
  451. for e in data['mainnet']:
  452. proto,coin = e[0],e[1]
  453. if proto[0] in '0123456789': proto = 'X_'+proto
  454. if proto+'Protocol' in globals(): continue
  455. if coin.lower() in CoinProtocol.coins: continue
  456. out += fs.format(coin.lower(),proto,('None',proto+'TestnetProtocol')[coin in tn_coins])
  457. # print out
  458. return out
  459. def init_coin(coin):
  460. coin = coin.upper()
  461. g.coin = coin
  462. g.proto = CoinProtocol(coin,g.testnet)