protocol.py 19 KB

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