194 lines
6.7 KiB
Python
Executable file
194 lines
6.7 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
|
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
tool/coin.py: Cryptocoin routines for the 'mmgen-tool' utility
|
|
"""
|
|
|
|
from collections import namedtuple
|
|
generator_data = namedtuple('generator_data',['kg','ag'])
|
|
|
|
from .common import tool_cmd_base
|
|
|
|
from ..key import PrivKey
|
|
from ..addr import KeyGenerator,AddrGenerator,CoinAddr,MMGenAddrType
|
|
|
|
class tool_cmd(tool_cmd_base):
|
|
"""
|
|
cryptocoin key/address utilities
|
|
|
|
May require use of the '--coin', '--type' and/or '--testnet' options
|
|
|
|
Examples:
|
|
mmgen-tool --coin=ltc --type=bech32 wif2addr <wif key>
|
|
mmgen-tool --coin=zec --type=zcash_z randpair
|
|
"""
|
|
|
|
need_proto = True
|
|
need_addrtype = True
|
|
|
|
def _init_generators(self,arg=None):
|
|
return generator_data(
|
|
kg = KeyGenerator( self.proto, self.mmtype.pubkey_type ),
|
|
ag = AddrGenerator( self.proto, self.mmtype ),
|
|
)
|
|
|
|
def randwif(self):
|
|
"generate a random private key in WIF format"
|
|
from ..crypto import get_random
|
|
return PrivKey(
|
|
self.proto,
|
|
get_random(32),
|
|
pubkey_type = self.mmtype.pubkey_type,
|
|
compressed = self.mmtype.compressed ).wif
|
|
|
|
def randpair(self):
|
|
"generate a random private key/address pair"
|
|
gd = self._init_generators()
|
|
from ..crypto import get_random
|
|
privkey = PrivKey(
|
|
self.proto,
|
|
get_random(32),
|
|
pubkey_type = self.mmtype.pubkey_type,
|
|
compressed = self.mmtype.compressed )
|
|
return (
|
|
privkey.wif,
|
|
gd.ag.to_addr( gd.kg.gen_data(privkey) ))
|
|
|
|
def wif2hex(self,wifkey:'sstr'):
|
|
"convert a private key from WIF to hexadecimal format"
|
|
return PrivKey(
|
|
self.proto,
|
|
wif = wifkey ).hex()
|
|
|
|
def hex2wif(self,privhex:'sstr'):
|
|
"convert a private key from hexadecimal to WIF format"
|
|
return PrivKey(
|
|
self.proto,
|
|
bytes.fromhex(privhex),
|
|
pubkey_type = self.mmtype.pubkey_type,
|
|
compressed = self.mmtype.compressed ).wif
|
|
|
|
def wif2addr(self,wifkey:'sstr'):
|
|
"generate a coin address from a key in WIF format"
|
|
gd = self._init_generators()
|
|
privkey = PrivKey(
|
|
self.proto,
|
|
wif = wifkey )
|
|
return gd.ag.to_addr( gd.kg.gen_data(privkey) )
|
|
|
|
def wif2redeem_script(self,wifkey:'sstr'): # new
|
|
"convert a WIF private key to a Segwit P2SH-P2WPKH redeem script"
|
|
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
|
|
gd = self._init_generators()
|
|
privkey = PrivKey(
|
|
self.proto,
|
|
wif = wifkey )
|
|
return gd.ag.to_segwit_redeem_script( gd.kg.gen_data(privkey) )
|
|
|
|
def wif2segwit_pair(self,wifkey:'sstr'):
|
|
"generate a Segwit P2SH-P2WPKH redeem script and address from a WIF private key"
|
|
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
|
|
gd = self._init_generators()
|
|
data = gd.kg.gen_data(PrivKey(
|
|
self.proto,
|
|
wif = wifkey ))
|
|
return (
|
|
gd.ag.to_segwit_redeem_script(data),
|
|
gd.ag.to_addr(data) )
|
|
|
|
def _privhex2out(self,privhex:'sstr',output_pubhex=False):
|
|
gd = self._init_generators()
|
|
pk = PrivKey(
|
|
self.proto,
|
|
bytes.fromhex(privhex),
|
|
compressed = self.mmtype.compressed,
|
|
pubkey_type = self.mmtype.pubkey_type )
|
|
data = gd.kg.gen_data(pk)
|
|
return data.pubkey.hex() if output_pubhex else gd.ag.to_addr(data)
|
|
|
|
def privhex2addr(self,privhex:'sstr'):
|
|
"generate a coin address from raw hexadecimal private key data"
|
|
return self._privhex2out(privhex)
|
|
|
|
def privhex2pubhex(self,privhex:'sstr'): # new
|
|
"generate a hexadecimal public key from raw hexadecimal private key data"
|
|
return self._privhex2out(privhex,output_pubhex=True)
|
|
|
|
def pubhex2addr(self,pubkeyhex:'sstr'):
|
|
"convert a hexadecimal pubkey to an address"
|
|
if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys
|
|
pubkeyhex = '04' + pubkeyhex
|
|
from ..keygen import keygen_public_data
|
|
ag = AddrGenerator( self.proto, self.mmtype )
|
|
return ag.to_addr(keygen_public_data(
|
|
pubkey = bytes.fromhex(pubkeyhex),
|
|
viewkey_bytes = None,
|
|
pubkey_type = self.mmtype.pubkey_type,
|
|
compressed = self.mmtype.compressed,
|
|
))
|
|
|
|
def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new
|
|
"convert a hexadecimal pubkey to a Segwit P2SH-P2WPKH redeem script"
|
|
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
|
|
from ..proto.common import hash160
|
|
return self.proto.pubhash2redeem_script( hash160(bytes.fromhex(pubkeyhex)) ).hex()
|
|
|
|
def redeem_script2addr(self,redeem_script_hex:'sstr'): # new
|
|
"convert a Segwit P2SH-P2WPKH redeem script to an address"
|
|
assert self.mmtype.name == 'segwit', 'This command is meaningful only for --type=segwit'
|
|
assert redeem_script_hex[:4] == '0014', f'{redeem_script_hex!r}: invalid redeem script'
|
|
assert len(redeem_script_hex) == 44, f'{len(redeem_script_hex)//2} bytes: invalid redeem script length'
|
|
from ..proto.common import hash160
|
|
return self.proto.pubhash2addr(
|
|
hash160( bytes.fromhex(redeem_script_hex) ),
|
|
p2sh = True )
|
|
|
|
def pubhash2addr(self,pubhashhex:'sstr'):
|
|
"convert public key hash to address"
|
|
pubhash = bytes.fromhex(pubhashhex)
|
|
if self.mmtype.name == 'segwit':
|
|
return self.proto.pubhash2segwitaddr( pubhash )
|
|
elif self.mmtype.name == 'bech32':
|
|
return self.proto.pubhash2bech32addr( pubhash )
|
|
else:
|
|
return self.proto.pubhash2addr( pubhash, self.mmtype.addr_fmt=='p2sh' )
|
|
|
|
def addr2pubhash(self,addr:'sstr'):
|
|
"convert coin address to public key hash"
|
|
ap = self.proto.decode_addr(addr)
|
|
assert ap, f'coin address {addr!r} could not be parsed'
|
|
if ap.fmt not in MMGenAddrType.pkh_fmts:
|
|
from ..util import die
|
|
die(2,f'{ap.fmt} addresses cannot be converted to pubhash')
|
|
return ap.bytes.hex()
|
|
|
|
def addr2scriptpubkey(self,addr:'sstr'):
|
|
"convert coin address to scriptPubKey"
|
|
from ..base_proto.bitcoin.tx.base import addr2scriptPubKey
|
|
return addr2scriptPubKey( self.proto, CoinAddr(self.proto,addr) )
|
|
|
|
def scriptpubkey2addr(self,hexstr:'sstr'):
|
|
"convert scriptPubKey to coin address"
|
|
from ..base_proto.bitcoin.tx.base import scriptPubKey2addr
|
|
return scriptPubKey2addr( self.proto, hexstr )[0]
|
|
|
|
def eth_checksummed_addr(self,addr:'sstr'):
|
|
"create a checksummed Ethereum address"
|
|
from ..protocol import init_proto
|
|
return init_proto('eth').checksummed_addr(addr)
|