#!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution # Copyright (C) 2013 by philemon # # 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 . """ addr.py: Address generation/display routines for mmgen suite """ import sys from hashlib import sha256, sha512 from binascii import hexlify, unhexlify from mmgen.bitcoin import numtowif from mmgen.config import * def test_for_keyconv(): """ Test for the presence of 'keyconv' utility on system """ keyconv_exec = "keyconv" from subprocess import Popen, PIPE try: p = Popen([keyconv_exec, '-h'], stdout=PIPE, stderr=PIPE) except: sys.stderr.write(""" Executable '%s' unavailable. Falling back on (slow) internal ECDSA library. Please install '%s' from the %s package on your system for much faster address generation. """ % (keyconv_exec, keyconv_exec, "vanitygen")) return False else: return True def generate_addrs(seed, addrnums, opts): """ generate_addresses(start, end, seed, opts) => None Generate a Bitcoin address or addresses end based on a seed, optionally outputting secret keys The 'keyconv' utility will be used for address generation if installed. Otherwise an internal function is used Supported options: print_secret, no_addresses, no_keyconv, gen_what Addresses are returned in a list of dictionaries with the following keys: num, sec, wif, addr """ if not 'no_addresses' in opts: if 'no_keyconv' in opts or test_for_keyconv() == False: sys.stderr.write("Using (slow) internal ECDSA library for address generation\n") from mmgen.bitcoin import privnum2addr keyconv = "" else: from subprocess import Popen, PIPE keyconv = "keyconv" a,t_addrs,i,out = sorted(addrnums),len(addrnums),0,[] while a: seed = sha512(seed).digest(); i += 1 # round /i/ if i < a[0]: continue a.pop(0) sys.stderr.write("\rGenerating %s %s (%s of %s)" % (opts['gen_what'], i, t_addrs-len(a), t_addrs)) # Secret key is double sha256 of seed hash round /i/ sec = sha256(sha256(seed).digest()).hexdigest() wif = numtowif(int(sec,16)) el = { 'num': i } if not 'print_addresses_only' in opts: el['sec'] = sec el['wif'] = wif if not 'no_addresses' in opts: if keyconv: p = Popen([keyconv, wif], stdout=PIPE) addr = dict([j.split() for j in \ p.stdout.readlines()])['Address:'] else: addr = privnum2addr(int(sec,16)) el['addr'] = addr out.append(el) w = opts['gen_what'] if t_addrs == 1: import re w = re.sub('e*s$','',w) sys.stderr.write("\rGenerated %s %s%s\n"%(t_addrs, w, " "*15)) return out def generate_keys(seed, addrnums): o = {'no_addresses': True, 'gen_what': "keys"} return generate_addrs(seed, addrnums, o) def format_addr_data(addrlist, seed_chksum, opts): """ print_addresses(addrs, opts) => None Print out the addresses and/or keys generated by generate_addresses() By default, prints addresses only Output can be customized with the following command line options: print_secret no_addresses b16 """ start = addrlist[0]['num'] end = addrlist[-1]['num'] wif_msg = "" if ('b16' in opts and 'print_secret' in opts) \ or 'no_addresses' in opts: wif_msg = " (wif)" fa = "%s%%-%ss %%-%ss %%s" % ( " "*2, len(str(end)) + (0 if 'no_addresses' in opts else 1), (5 if 'print_secret' in opts else 1) + len(wif_msg) ) header = """ # MMGen address file # # This file is editable. # Everything following a hash symbol '#' is ignored. # A label may be added to the right of each address, and it will be # appended to the bitcoind wallet label upon import (max. {} characters, # allowed characters: A-Za-z0-9, plus '{}'). """.format(max_wallet_addr_label_len, "', '".join(wallet_addr_label_symbols)).strip() data = [] if not 'stdout' in opts: data.append(header + "\n") data.append("%s {" % seed_chksum.upper()) for el in addrlist: col1 = el['num'] if 'no_addresses' in opts: if 'b16' in opts: data.append(fa % (col1, " (hex):", el['sec'])) col1 = "" data.append(fa % (col1, " (wif):", el['wif'])) if 'b16' in opts: data.append("") elif 'print_secret' in opts: if 'b16' in opts: data.append(fa % (col1, "sec (hex):", el['sec'])) col1 = "" data.append(fa % (col1, "sec"+wif_msg+":", el['wif'])) data.append(fa % ("", "addr:", el['addr'])) data.append("") else: data.append(fa % (col1, "", el['addr'])) if not data[-1]: data.pop() data.append("}") return "\n".join(data) + "\n" def fmt_addr_list(addr_list): prev = addr_list[0] ret = prev, for i in addr_list[1:]: if i == prev + 1: if i == addr_list[-1]: ret += "-", i else: if prev != ret[-1]: ret += "-", prev ret += ",", i prev = i return "".join([str(i) for i in ret]) def write_addr_data_to_file(seed, data, addr_list, opts): if 'print_addresses_only' in opts: ext = "addrs" elif 'no_addresses' in opts: ext = "keys" else: ext = "akeys" if 'b16' in opts: ext = ext.replace("keys","xkeys") from mmgen.utils import write_to_file, make_chksum_8, msg outfile = "{}[{}].{}".format( make_chksum_8(seed), fmt_addr_list(addr_list), ext ) if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) write_to_file(outfile,data) dtype = "Address" if 'print_addresses_only' in opts else "Key" msg("%s data saved to file '%s'" % (dtype,outfile)) if __name__ == "__main__": print fmt_addr_list(sorted(set([1,3,5,2,8,9,10,12,13,14,16])))