#!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution # Copyright (C)2013-2023 The MMGen Project # # 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 . """ tool.coin: 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 CoinAddr,MMGenAddrType from ..addrgen import KeyGenerator,AddrGenerator 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 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.cfg, self.proto, self.mmtype.pubkey_type ), ag = AddrGenerator( self.cfg, self.proto, self.mmtype ), ) def randwif(self): "generate a random private key in WIF format" from ..crypto import Crypto return PrivKey( self.proto, Crypto(self.cfg).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 Crypto privkey = PrivKey( self.proto, Crypto(self.cfg).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.cfg, 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.btc.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.btc.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 ..proto.btc.tx.base import addr2scriptPubKey return addr2scriptPubKey( self.proto, CoinAddr(self.proto,addr) ) def scriptpubkey2addr(self,hexstr:'sstr'): "convert scriptPubKey to coin address" from ..proto.btc.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( self.cfg, 'eth' ).checksummed_addr(addr)