coin.py 6.4 KB

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