protocol.py 19 KB

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