protocol.py 22 KB

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