coin.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2022 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. tool/coin.py: Cryptocoin routines for the 'mmgen-tool' utility
  20. """
  21. from collections import namedtuple
  22. generator_data = namedtuple('generator_data',['kg','ag'])
  23. from .common import tool_cmd_base
  24. from ..key import PrivKey
  25. from ..addr import CoinAddr,MMGenAddrType
  26. from ..addrgen import KeyGenerator,AddrGenerator
  27. class tool_cmd(tool_cmd_base):
  28. """
  29. cryptocoin key/address utilities
  30. May require use of the '--coin', '--type' and/or '--testnet' options
  31. Examples:
  32. mmgen-tool --coin=ltc --type=bech32 wif2addr <wif key>
  33. mmgen-tool --coin=zec --type=zcash_z randpair
  34. """
  35. need_proto = True
  36. need_addrtype = True
  37. def _init_generators(self,arg=None):
  38. return generator_data(
  39. kg = KeyGenerator( self.proto, self.mmtype.pubkey_type ),
  40. ag = AddrGenerator( self.proto, self.mmtype ),
  41. )
  42. def randwif(self):
  43. "generate a random private key in WIF format"
  44. from ..crypto import get_random
  45. return PrivKey(
  46. self.proto,
  47. get_random(32),
  48. pubkey_type = self.mmtype.pubkey_type,
  49. compressed = self.mmtype.compressed ).wif
  50. def randpair(self):
  51. "generate a random private key/address pair"
  52. gd = self._init_generators()
  53. from ..crypto import get_random
  54. privkey = PrivKey(
  55. self.proto,
  56. get_random(32),
  57. pubkey_type = self.mmtype.pubkey_type,
  58. compressed = self.mmtype.compressed )
  59. return (
  60. privkey.wif,
  61. gd.ag.to_addr( gd.kg.gen_data(privkey) ))
  62. def wif2hex(self,wifkey:'sstr'):
  63. "convert a private key from WIF to hexadecimal format"
  64. return PrivKey(
  65. self.proto,
  66. wif = wifkey ).hex()
  67. def hex2wif(self,privhex:'sstr'):
  68. "convert a private key from hexadecimal to WIF format"
  69. return PrivKey(
  70. self.proto,
  71. bytes.fromhex(privhex),
  72. pubkey_type = self.mmtype.pubkey_type,
  73. compressed = self.mmtype.compressed ).wif
  74. def wif2addr(self,wifkey:'sstr'):
  75. "generate a coin address from a key in WIF format"
  76. gd = self._init_generators()
  77. privkey = PrivKey(
  78. self.proto,
  79. wif = wifkey )
  80. return gd.ag.to_addr( gd.kg.gen_data(privkey) )
  81. def wif2redeem_script(self,wifkey:'sstr'): # new
  82. "convert a WIF private key to a Segwit P2SH-P2WPKH redeem script"
  83. assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
  84. gd = self._init_generators()
  85. privkey = PrivKey(
  86. self.proto,
  87. wif = wifkey )
  88. return gd.ag.to_segwit_redeem_script( gd.kg.gen_data(privkey) )
  89. def wif2segwit_pair(self,wifkey:'sstr'):
  90. "generate a Segwit P2SH-P2WPKH redeem script and address from a WIF private key"
  91. assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
  92. gd = self._init_generators()
  93. data = gd.kg.gen_data(PrivKey(
  94. self.proto,
  95. wif = wifkey ))
  96. return (
  97. gd.ag.to_segwit_redeem_script(data),
  98. gd.ag.to_addr(data) )
  99. def _privhex2out(self,privhex:'sstr',output_pubhex=False):
  100. gd = self._init_generators()
  101. pk = PrivKey(
  102. self.proto,
  103. bytes.fromhex(privhex),
  104. compressed = self.mmtype.compressed,
  105. pubkey_type = self.mmtype.pubkey_type )
  106. data = gd.kg.gen_data(pk)
  107. return data.pubkey.hex() if output_pubhex else gd.ag.to_addr(data)
  108. def privhex2addr(self,privhex:'sstr'):
  109. "generate a coin address from raw hexadecimal private key data"
  110. return self._privhex2out(privhex)
  111. def privhex2pubhex(self,privhex:'sstr'): # new
  112. "generate a hexadecimal public key from raw hexadecimal private key data"
  113. return self._privhex2out(privhex,output_pubhex=True)
  114. def pubhex2addr(self,pubkeyhex:'sstr'):
  115. "convert a hexadecimal pubkey to an address"
  116. if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys
  117. pubkeyhex = '04' + pubkeyhex
  118. from ..keygen import keygen_public_data
  119. ag = AddrGenerator( self.proto, self.mmtype )
  120. return ag.to_addr(keygen_public_data(
  121. pubkey = bytes.fromhex(pubkeyhex),
  122. viewkey_bytes = None,
  123. pubkey_type = self.mmtype.pubkey_type,
  124. compressed = self.mmtype.compressed,
  125. ))
  126. def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new
  127. "convert a hexadecimal pubkey to a Segwit P2SH-P2WPKH redeem script"
  128. assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
  129. from ..proto.btc.common import hash160
  130. return self.proto.pubhash2redeem_script( hash160(bytes.fromhex(pubkeyhex)) ).hex()
  131. def redeem_script2addr(self,redeem_script_hex:'sstr'): # new
  132. "convert a Segwit P2SH-P2WPKH redeem script to an address"
  133. assert self.mmtype.name == 'segwit', 'This command is meaningful only for --type=segwit'
  134. assert redeem_script_hex[:4] == '0014', f'{redeem_script_hex!r}: invalid redeem script'
  135. assert len(redeem_script_hex) == 44, f'{len(redeem_script_hex)//2} bytes: invalid redeem script length'
  136. from ..proto.btc.common import hash160
  137. return self.proto.pubhash2addr(
  138. hash160( bytes.fromhex(redeem_script_hex) ),
  139. p2sh = True )
  140. def pubhash2addr(self,pubhashhex:'sstr'):
  141. "convert public key hash to address"
  142. pubhash = bytes.fromhex(pubhashhex)
  143. if self.mmtype.name == 'segwit':
  144. return self.proto.pubhash2segwitaddr( pubhash )
  145. elif self.mmtype.name == 'bech32':
  146. return self.proto.pubhash2bech32addr( pubhash )
  147. else:
  148. return self.proto.pubhash2addr( pubhash, self.mmtype.addr_fmt=='p2sh' )
  149. def addr2pubhash(self,addr:'sstr'):
  150. "convert coin address to public key hash"
  151. ap = self.proto.decode_addr(addr)
  152. assert ap, f'coin address {addr!r} could not be parsed'
  153. if ap.fmt not in MMGenAddrType.pkh_fmts:
  154. from ..util import die
  155. die(2,f'{ap.fmt} addresses cannot be converted to pubhash')
  156. return ap.bytes.hex()
  157. def addr2scriptpubkey(self,addr:'sstr'):
  158. "convert coin address to scriptPubKey"
  159. from ..proto.btc.tx.base import addr2scriptPubKey
  160. return addr2scriptPubKey( self.proto, CoinAddr(self.proto,addr) )
  161. def scriptpubkey2addr(self,hexstr:'sstr'):
  162. "convert scriptPubKey to coin address"
  163. from ..proto.btc.tx.base import scriptPubKey2addr
  164. return scriptPubKey2addr( self.proto, hexstr )[0]
  165. def eth_checksummed_addr(self,addr:'sstr'):
  166. "create a checksummed Ethereum address"
  167. from ..protocol import init_proto
  168. return init_proto('eth').checksummed_addr(addr)