diff --git a/mmgen-txcreate b/mmgen-txcreate index fda40fa3..d1782331 100755 --- a/mmgen-txcreate +++ b/mmgen-txcreate @@ -82,7 +82,15 @@ c = connect_to_bitcoind() if not 'quiet' in opts and not 'info' in opts: do_license_msg() -unspent = sort_and_view(c.listunspent()) +# Begin test +# import mmgen.rpc +# us = eval(get_data_from_file("listunspent.json")) +# End test + +us = c.listunspent() +# write_to_file("listunspent.json",repr(us)) +# sys.exit() +unspent = sort_and_view(us) total = trim_exponent(sum([i.amount for i in unspent])) diff --git a/mmgen-txsign b/mmgen-txsign index 5d419e20..174757dc 100755 --- a/mmgen-txsign +++ b/mmgen-txsign @@ -31,7 +31,7 @@ from mmgen.utils import * help_data = { 'prog_name': sys.argv[0].split("/")[-1], 'desc': "Sign a Bitcoin transaction generated by mmgen-txcreate", - 'usage': "[opts] [mmgen wallet/seed/mnemonic file]", + 'usage': "[opts] [mmgen wallet/seed/words/brain file]...", 'options': """ -h, --help Print this help message -d, --outdir d Specify an alternate directory 'd' for output @@ -59,8 +59,15 @@ prompted to enter the data. In cases of transactions with mixed mmgen and non-mmgen inputs, non-mmgen keys must be supplied in a separate file (WIF format, one key per line) using the '--keys-from-file' option. Alternatively, one may import the -required mmgen keys into wallet.dat and use the '--force-wallet-dat' option. -""".format(seed_ext) +required mmgen keys into the bitcoind wallet.dat and use the +'--force-wallet-dat' option. + +Seed data supplied in files must have the following extensions: + wallet: '.{}' + seed: '.{}' + mnemonic: '.{}' + brainwallet: '.{}' +""".format(seed_ext,wallet_ext,seed_ext,mn_ext,brain_ext) } short_opts = "hd:efik:qb:ms" @@ -77,15 +84,15 @@ if debug: print "Processed options: %s" % repr(opts) print "Cmd args: %s" % repr(cmd_args) -if len(cmd_args) in (1,2): - tx_file = cmd_args[0] - check_infile(tx_file) +if len(cmd_args) > 0: + for i in cmd_args: check_infile(i) else: usage(help_data) # Begin execution c = connect_to_bitcoind() +tx_file = cmd_args.pop(0) tx_data = get_lines_from_file(tx_file,"transaction data") metadata,tx_hex,sig_data,inputs_data = parse_tx_data(tx_data,tx_file) @@ -101,25 +108,8 @@ msg("Successfully opened transaction file '%s'" % tx_file) if user_confirm("View transaction data? ",default_yes=False): view_tx_data(c,inputs_data,tx_hex,metadata) - -def sign_transaction(tx_hex,sig_data,keys=None): - - from mmgen.rpc import exceptions - - try: - sig_tx = c.signrawtransaction(tx_hex,sig_data,keys) - except exceptions.InvalidAddressOrKey: - msg("Invalid address or key") - sys.exit(3) -# except: -# msg("Failed to sign transaction") -# sys.exit(3) - - return sig_tx - - # Are inputs mmgen addresses? -infile,mmgen_addrs,other_addrs = "",[],[] +infile,mmgen_addrs,other_addrs,keys = "",[],[],[] for i in inputs_data: if verify_mmgen_label(i['account']): @@ -129,32 +119,46 @@ for i in inputs_data: if mmgen_addrs and not 'force_wallet_dat' in opts: # Check that all the seed IDs are the same: - a_ids = list(set([i['account'][:8] for i in mmgen_addrs])) - if len(a_ids) != 1: - msg("Addresses come from different seeds! (%s)" % " ".join(a_ids)) - sys.exit(3) + seed_ids = list(set([i['account'][:8] for i in mmgen_addrs])) + while seed_ids: + if cmd_args: + infile = cmd_args.pop() + ext = infile.split(".")[-1] + for e,o in ( + (wallet_ext, {}), + (mn_ext, {"from_mnemonic":True}), + (seed_ext, {"from_seed": True}), + (brain_ext, {}) + ): + if e == ext: + if e == brain_ext: + if "from_brain" in opts: o = opts + else: + msg("'--from-brain' option must be specified for brainwallet file") + sys.exit(2) + seed = get_seed(infile,o) + elif "from_brain" in opts or "from_mnemonic" in opts or "from_seed" in opts: + msg("Need data for seed ID %s" % seed_ids[0]) + seed = get_seed("",opts) + else: + msg("One of '-b', '-m' or '-s' must be specified for seed IDs: %s" % + " ".join(seed_ids)) + sys.exit(2) - if len(cmd_args) == 2: - infile = cmd_args[1] - elif "from_brain" in opts or "from_mnemonic" in opts or "from_seed" in opts: - infile = "" - else: - msg("Inputs contain mmgen addresses. An MMGen wallet file must be specified on the command line (or use the '-b', '-m' or '-s' options).".strip()) - sys.exit(2) + seed_id = make_chksum_8(seed) + if seed_id in seed_ids: + seed_ids.remove(seed_id) + seed_id_addrs = [] + for i in mmgen_addrs: + if i['account'][:8] == seed_id: + seed_id_addrs.append(int(i['account'].split()[0][9:])) - seed = get_seed(infile,opts) - seed_id = make_chksum_8(seed) - - if seed_id != a_ids[0]: - msg("Seed ID of wallet (%s) doesn't match that of addresses (%s)" - % (seed_id,a_ids[0])) - sys.exit(3) - - addr_nums = [int(i['account'].split()[0][9:]) for i in mmgen_addrs] - - from mmgen.addr import generate_addrs - o = {'no_addresses': True, 'gen_what': "keys"} - keys = [i['wif'] for i in generate_addrs(seed, addr_nums, o)] + from mmgen.addr import generate_addrs + o = {'no_addresses': True, 'gen_what': "keys"} + keys += [i['wif'] for i in generate_addrs(seed, seed_id_addrs, o)] + else: + msg("Supplied seed ID (%s) is incorrect" % seed_id) + sys.exit(2) if other_addrs: if 'keys_from_file' in opts: @@ -169,12 +173,12 @@ address%s: %s""" % ( )) sys.exit(2) - sig_tx = sign_transaction(tx_hex,sig_data,keys) + sig_tx = sign_transaction(c,tx_hex,sig_data,keys) elif 'keys_from_file' in opts: keys = get_lines_from_file(opts['keys_from_file'],"key data") - sig_tx = sign_transaction(tx_hex,sig_data,keys) + sig_tx = sign_transaction(c,tx_hex,sig_data,keys) else: prompt = "Enter passphrase for bitcoind wallet: " if 'echo_passphrase' in opts: @@ -198,7 +202,7 @@ else: else: msg("Passphrase OK") - sig_tx = sign_transaction(tx_hex,sig_data) + sig_tx = sign_transaction(c,tx_hex,sig_data) if wallet_enc: c.walletlock() @@ -207,7 +211,7 @@ else: if sig_tx['complete']: msg("Signing completed") else: - msg("Not all keys could be found to sign this transaction") + msg("Some keys were missing. Transaction could not be signed.") sys.exit(3) prompt = "Save signed transaction?" diff --git a/mmgen/addr.py b/mmgen/addr.py index ba90bca7..5f025408 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -107,7 +107,9 @@ def generate_addrs(seed, addrnums, opts): out.append(el) - sys.stderr.write("\rGenerated %s %s%s\n"%(t_addrs,opts['gen_what']," "*15)) + w = opts['gen_what'] + if t_addrs == 1: w = w[:-1] + sys.stderr.write("\rGenerated %s %s%s\n"%(t_addrs, w, " "*15)) return out diff --git a/mmgen/config.py b/mmgen/config.py index c2d2b80f..a5e1fa03 100755 --- a/mmgen/config.py +++ b/mmgen/config.py @@ -19,7 +19,10 @@ config.py: Constants and configuration options for the mmgen suite """ proj_name = "mmgen" +wallet_ext = "mmdat" seed_ext = "mmseed" +mn_ext = "mmwords" +brain_ext = "mmbrain" default_wl = "electrum" #default_wl = "tirosh" diff --git a/mmgen/rpc/proxy.py b/mmgen/rpc/proxy.py index 0d4b6dd7..b17ba11d 100755 --- a/mmgen/rpc/proxy.py +++ b/mmgen/rpc/proxy.py @@ -91,11 +91,16 @@ class AuthServiceProxy(object): 'method': self.__serviceName, 'params': args, 'id': self.__idcnt}) - self.__conn.request('POST', self.__url.path, postdata, - { 'Host' : self.__url.hostname, - 'User-Agent' : USER_AGENT, - 'Authorization' : self.__authhdr, - 'Content-type' : 'application/json' }) + try: + self.__conn.request('POST', self.__url.path, postdata, + { 'Host' : self.__url.hostname, + 'User-Agent' : USER_AGENT, + 'Authorization' : self.__authhdr, + 'Content-type' : 'application/json' }) + except: + print "Unable to connect to bitcoind. Exiting" + import sys + sys.exit(2) httpresp = self.__conn.getresponse() if httpresp is None: diff --git a/mmgen/tx.py b/mmgen/tx.py index cd513535..24438703 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -120,13 +120,13 @@ def get_cfg_options(cfg_keys): def print_tx_to_file(tx,sel_unspent,send_amt,opts): - sig_data = [{"txid":i.txid,"vout":i.vout,"scriptPubKey":i.scriptPubKey} - for i in sel_unspent] tx_id = make_chksum_6(unhexlify(tx)).upper() outfile = "tx_%s[%s].raw" % (tx_id,send_amt) if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) metadata = "%s %s %s" % (tx_id, send_amt, make_timestamp()) + sig_data = [{"txid":i.txid,"vout":i.vout,"scriptPubKey":i.scriptPubKey} + for i in sel_unspent] data = "%s\n%s\n%s\n%s\n" % ( metadata, tx, repr(sig_data), repr([i.__dict__ for i in sel_unspent]) @@ -211,7 +211,7 @@ def sort_and_view(unspent): """\n Sort options: [t]xid, [a]mount, a[d]dress, [A]ge, [r]everse, [M]mgen addr View options: [g]roup, show [m]mgen addr -(Type 'q' to quit sorting): """).strip() +(Type 'q' to quit sorting, 'p' to print to file): """).strip() if reply == 'a': unspent.sort(s_amt); sort = "amount"; break elif reply == 't': unspent.sort(s_txid); sort = "txid"; break elif reply == 'd': unspent.sort(s_addr); sort = "address"; break @@ -223,11 +223,16 @@ View options: [g]roup, show [m]mgen addr break elif reply == 'g': group = False if group else True; break elif reply == 'm': mmaddr = False if mmaddr else True; break + elif reply == 'p': + f = "listunspent.out" + write_to_file(f,"\n".join(output)+"\n") + msg("\nData written to '%s'" % f) + sys.exit(1) elif reply == 'q': break else: msg("Invalid input") msg("\n") - if reply == 'q': break + if reply in 'q': break return tuple(unspent) @@ -426,3 +431,23 @@ def parse_addrs_file(f): return seed_id,ret sys.exit(3) + + +def sign_transaction(c,tx_hex,sig_data,keys=None): + + if keys: + msg("%s keys total" % len(keys)) + if debug: print "Keys:\n %s" % "\n ".join(keys) + + from mmgen.rpc import exceptions + + try: + sig_tx = c.signrawtransaction(tx_hex,sig_data,keys) + except exceptions.InvalidAddressOrKey: + msg("Invalid address or key") + sys.exit(3) +# except: +# msg("Failed to sign transaction") +# sys.exit(3) + + return sig_tx diff --git a/mmgen/utils.py b/mmgen/utils.py index 1a0d0a8f..0a6c3687 100755 --- a/mmgen/utils.py +++ b/mmgen/utils.py @@ -473,7 +473,7 @@ def write_seed(seed, opts): def write_mnemonic(mn, seed, opts): - outfile = "%s.words" % make_chksum_8(seed).upper() + outfile = "%s.%s" % (make_chksum_8(seed).upper(),mn_ext) if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) @@ -524,7 +524,7 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts): hash_preset = opts['hash_preset'] - outfile = "{}-{}[{},{}].dat".format(seed_id,key_id,seed_len,hash_preset) + outfile="{}-{}[{},{}].{}".format(seed_id,key_id,seed_len,hash_preset,wallet_ext) if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) @@ -686,6 +686,14 @@ def get_lines_from_file(infile,what): return lines +def get_data_from_file(infile,what="data"): + msg("Getting %s from file '%s'" % (what,infile)) + f = open_file_or_exit(infile,'r') + data = f.read() + f.close() + return data + + def get_words(infile,what,prompt,opts): if infile: words = _get_words_from_file(infile,what) @@ -785,7 +793,6 @@ def get_seed(infile,opts,no_wallet=False): from mmgen.mnemonic import get_seed_from_mnemonic return get_seed_from_mnemonic(words,wl) elif 'from_brain' in opts: - msg("") if 'quiet' not in opts: confirm_or_exit( cmessages['brain_warning'].format( @@ -804,6 +811,7 @@ def get_seed(infile,opts,no_wallet=False): else: return get_seed_from_wallet(infile, opts) + def remove_blanks_comments(lines): import re # re.sub(pattern, repl, string, count=0, flags=0)