protocol.py 20 KB

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