From 8556de2101c35c82f6455cade71426e1270b01ff Mon Sep 17 00:00:00 2001 From: philemon Date: Sun, 14 May 2017 21:54:05 +0300 Subject: [PATCH] Pre-RBF changes, bugfixes --- mmgen/main_addrimport.py | 11 +++++----- mmgen/main_txcreate.py | 2 +- mmgen/main_txdo.py | 12 +++++----- mmgen/main_wallet.py | 21 +++++++++--------- mmgen/obj.py | 9 ++++++-- mmgen/test.py | 2 +- mmgen/tool.py | 47 +++++++++++++++++++++------------------- mmgen/tx.py | 22 ++++++++++++++----- mmgen/txcreate.py | 4 ++-- mmgen/txsign.py | 2 +- mmgen/util.py | 4 ++-- test/test.py | 11 +++++----- 12 files changed, 84 insertions(+), 63 deletions(-) diff --git a/mmgen/main_addrimport.py b/mmgen/main_addrimport.py index 3447a404..2a19412c 100755 --- a/mmgen/main_addrimport.py +++ b/mmgen/main_addrimport.py @@ -83,14 +83,13 @@ if not opt.test: m = """ WARNING: You've chosen the '--rescan' option. Rescanning the blockchain is necessary only if an address you're importing is already on the blockchain, -has a balance and is not already in your tracking wallet. Note that the -rescanning process is very slow (>30 min. for each imported address on a -low-powered computer). +has a balance and is not in your tracking wallet. Note that the rescanning +process is very slow (>30 min. for each imported address on a low-powered +computer). """.strip() if opt.rescan else """ WARNING: If any of the addresses you're importing is already on the blockchain, -has a balance and is not already in your tracking wallet, you must exit the -program now and rerun it using the '--rescan' option. Otherwise you may ignore -this message and continue. +has a balance and is not in your tracking wallet, you must exit the program now +and rerun it using the '--rescan' option. """.strip() if not opt.quiet: confirm_or_exit(m, 'continue', expect='YES') diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index a8d73cbf..45adebdc 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -35,8 +35,8 @@ opts_data = { -C, --tx-confs= c Desired number of confirmations (default: {g.tx_confs}) -d, --outdir= d Specify an alternate directory 'd' for output -f, --tx-fee= f Transaction fee (default: {g.tx_fee} BTC (but see below)) --m, --minconf= n Minimum number of confirmations required to spend outputs (default: 1) -i, --info Display unspent outputs and exit +-m, --minconf= n Minimum number of confirmations required to spend outputs (default: 1) -q, --quiet Suppress warnings; overwrite files without prompting -v, --verbose Produce more verbose output """.format(g=g), diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index 96779bd9..fcfd5378 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -30,8 +30,8 @@ opts_data = { -h, --help Print this help message --, --longhelp Print help message for long options (common options) -a, --tx-fee-adj= f Adjust transaction fee by factor 'f' (see below) --b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brainwallet - input +-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for + brainwallet input -B, --no-blank Don't blank screen before displaying unspent outputs -c, --comment-file= f Source the transaction's comment from file 'f' -C, --tx-confs= c Desired number of confirmations (default: {g.tx_confs}) @@ -46,8 +46,10 @@ opts_data = { with non-standard (< {g.seed_len}-bit) seed lengths. -k, --keys-from-file=f Provide additional keys for non-{pnm} addresses -K, --key-generator= m Use method 'm' for public key generation - Options: {kgs} (default: {kg}) --m, --minconf=n Minimum number of confirmations required to spend outputs (default: 1) + Options: {kgs} + (default: {kg}) +-m, --minconf=n Minimum number of confirmations required to spend + outputs (default: 1) -M, --mmgen-keys-from-file=f Provide keys for {pnm} addresses in a key- address file (output of '{pnl}-keygen'). Permits online signing without an {pnm} seed source. The @@ -56,7 +58,7 @@ opts_data = { -O, --old-incog-fmt Specify old-format incognito input -p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p' for password hashing (default: '{g.hash_preset}') --P, --passwd-file= f Get {pnm} wallet or bitcoind passphrase from file 'f' +-P, --passwd-file= f Get {pnm} wallet passphrase from file 'f' -q, --quiet Suppress warnings; overwrite files without prompting -v, --verbose Produce more verbose output -z, --show-hash-presets Show information on available hash presets diff --git a/mmgen/main_wallet.py b/mmgen/main_wallet.py index 31fbf7fd..6b221109 100755 --- a/mmgen/main_wallet.py +++ b/mmgen/main_wallet.py @@ -20,16 +20,12 @@ mmgen/main_wallet: Entry point for MMGen wallet-related scripts """ -import os,re - +import os from mmgen.common import * from mmgen.seed import SeedSource,Wallet from mmgen.filename import find_file_in_dir from mmgen.obj import MMGenWalletLabel -bn = os.path.basename(sys.argv[0]) -invoked_as = re.sub(r'^wallet','',bn.split('-')[-1]) - usage = '[opts] [infile]' nargs = 1 iaction = 'convert' @@ -37,6 +33,8 @@ oaction = 'convert' bw_note = opts.bw_note pw_note = opts.pw_note +invoked_as = 'passchg' if g.prog_name == 'mmgen-passchg' else g.prog_name.partition('-wallet')[2] + # full: defhHiJkKlLmoOpPqrSvz- if invoked_as == 'gen': desc = 'Generate an {pnm} wallet from a random seed' @@ -57,7 +55,7 @@ elif invoked_as == 'passchg': iaction = 'input' bw_note = '' else: - die(1,"'%s': unrecognized invocation" % bn) + die(1,"'%s': unrecognized invocation" % g.prog_name) opts_data = { # Can't use: share/Opts doesn't know anything about fmt codes @@ -74,9 +72,9 @@ opts_data = { -o, --out-fmt= f {oaction} to wallet format 'f' (see FMT CODES below) -H, --hidden-incog-input-params=f,o Read hidden incognito data from file 'f' at offset 'o' (comma-separated) --J, --hidden-incog-output-params=f,o Write hidden incognito data to file 'f' - at offset 'o' (comma-separated). File 'f' will be cre- - ated and filled with random data if it doesn't exist. +-J, --hidden-incog-output-params=f,o Write hidden incognito data to file + 'f' at offset 'o' (comma-separated). File 'f' will be + created if necessary and filled with random data. -O, --old-incog-fmt Specify old-format incognito input -k, --keep-passphrase Reuse passphrase of input wallet for output wallet -K, --keep-hash-preset Reuse hash preset of input wallet for output wallet @@ -128,7 +126,10 @@ if invoked_as in ('conv','passchg'): ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg')) -if invoked_as == 'chk': sys.exit() +if invoked_as == 'chk': + vmsg('Wallet label: {}'.format(ss_in.ssdata.label.hl())) + # TODO: display creation date + sys.exit() if invoked_as in ('conv','passchg'): msg(green('Processing output wallet')) diff --git a/mmgen/obj.py b/mmgen/obj.py index b77d6037..252b6eb2 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -243,7 +243,8 @@ class Hilite(object): trunc_ok = True @classmethod - def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None,center=False,nullrepl=''): + def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None, + center=False,nullrepl='',app='',appcolor=False): if width == None: width = cls.width if trunc_ok == None: trunc_ok = cls.trunc_ok assert width > 0 @@ -253,7 +254,11 @@ class Hilite(object): assert type(encl) is str and len(encl) in (0,2) a,b = list(encl) if encl else ('','') if trunc_ok and len(s) > width: s = s[:width] - return cls.colorize((a+s+b).ljust(width),color=color) + if app: + return cls.colorize(a+s+b,color=color) + \ + cls.colorize(app.ljust(width-len(a+s+b)),color=appcolor) + else: + return cls.colorize((a+s+b).ljust(width),color=color) def fmt(self,*args,**kwargs): assert args == () # forbid invocation w/o keywords diff --git a/mmgen/test.py b/mmgen/test.py index 76a8e6dd..e544388b 100755 --- a/mmgen/test.py +++ b/mmgen/test.py @@ -38,7 +38,7 @@ def cleandir(d): rmtree(os.path.join(d,f)) def getrandnum(n): return int(hexlify(os.urandom(n)),16) -def getrandhex(n): return hexlify(os.urandom(n)).lstrip('0') +def getrandhex(n): return hexlify(os.urandom(n)) def getrandstr(num_chars,no_space=False): n,m = 95,32 if no_space: n,m = 94,33 diff --git a/mmgen/tool.py b/mmgen/tool.py index 79648aa3..05158ddf 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -22,7 +22,7 @@ tool.py: Routines and data for the 'mmgen-tool' utility import binascii as ba -import mmgen.bitcoin as bitcoin +import mmgen.bitcoin as mmb from mmgen.common import * from mmgen.crypto import * from mmgen.tx import * @@ -267,17 +267,17 @@ def unhexdump(infile): get_data_from_file(infile,dash=True,silent=True))) def strtob58(s): - enc = bitcoin.b58encode(s) - dec = bitcoin.b58decode(enc) + enc = mmb.b58encode(s) + dec = mmb.b58decode(enc) print_convert_results(s,enc,dec,'str') -def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode): +def hextob58(s,f_enc=mmb.b58encode, f_dec=mmb.b58decode): s = s.strip() enc = f_enc(ba.unhexlify(s)) dec = ba.hexlify(f_dec(enc)) print_convert_results(s,enc,dec,'hex') -def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode): +def b58tohex(s,f_enc=mmb.b58decode, f_dec=mmb.b58encode): s = s.strip() tmp = f_enc(s) if tmp == False: die(1,"Unable to decode string '%s'" % s) @@ -285,7 +285,7 @@ def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode): dec = f_dec(ba.unhexlify(enc)) print_convert_results(s,enc,dec,'b58') -def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode): +def b58tostr(s,f_enc=mmb.b58decode, f_dec=mmb.b58encode): s = s.strip() enc = f_enc(s) if enc == False: die(1,"Unable to decode string '%s'" % s) @@ -294,8 +294,8 @@ def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode): def b58randenc(): r = get_random(32) - enc = bitcoin.b58encode(r) - dec = bitcoin.b58decode(enc) + enc = mmb.b58encode(r) + dec = mmb.b58decode(enc) print_convert_results(r,enc,dec,'str') def randhex(nbytes='32'): @@ -303,23 +303,23 @@ def randhex(nbytes='32'): def randwif(compressed=False): r_hex = ba.hexlify(get_random(32)) - enc = bitcoin.hex2wif(r_hex,compressed) - dec = bitcoin.wif2hex(enc) + enc = mmb.hex2wif(r_hex,compressed) + dec = mmb.wif2hex(enc) print_convert_results(r_hex,enc,dec,'hex') def randpair(compressed=False): r_hex = ba.hexlify(get_random(32)) - wif = bitcoin.hex2wif(r_hex,compressed) - addr = bitcoin.privnum2addr(int(r_hex,16),compressed) + wif = mmb.hex2wif(r_hex,compressed) + addr = mmb.privnum2addr(int(r_hex,16),compressed) Vmsg('Key (hex): %s' % r_hex) Vmsg_r('Key (WIF): '); Msg(wif) Vmsg_r('Addr: '); Msg(addr) def wif2addr(wif,compressed=False): - s_enc = bitcoin.wif2hex(wif) + s_enc = mmb.wif2hex(wif) if s_enc == False: die(1,'Invalid address') - addr = bitcoin.privnum2addr(int(s_enc,16),compressed) + addr = mmb.privnum2addr(int(s_enc,16),compressed) Vmsg_r('Addr: '); Msg(addr) wordlists = 'electrum','tirosh' @@ -433,7 +433,10 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa )] old_sid = '' - def s_mmgen(k): return '{:>0{w}}'.format(k,w=AddrIdx.max_digits+9) # TODO + def s_mmgen(k): + s = k.split('_') + a,b = s if len(s) == 2 else (k,'') + return '{}_{:>0{w}}'.format(a,b,w=AddrIdx.max_digits+9) for k in sorted(addrs,key=s_mmgen): if old_sid and old_sid != k.split('_')[0]: out.append('') old_sid = k.split('_')[0] @@ -517,25 +520,25 @@ def sha256x2(s, file_input=False, hex_input=False): Msg(sha256(sha256(b).digest()).hexdigest()) def hexaddr2addr(hexaddr): - Msg(bitcoin.hexaddr2addr(hexaddr)) + Msg(mmb.hexaddr2addr(hexaddr)) def addr2hexaddr(addr): - Msg(bitcoin.verify_addr(addr,return_hex=True)) + Msg(mmb.verify_addr(addr,return_hex=True)) def pubkey2hexaddr(pubkeyhex): - Msg(bitcoin.pubhex2hexaddr(pubkeyhex)) + Msg(mmb.pubhex2hexaddr(pubkeyhex)) def pubkey2addr(pubkeyhex): - Msg(bitcoin.hexaddr2addr(bitcoin.pubhex2hexaddr(pubkeyhex))) + Msg(mmb.hexaddr2addr(mmb.pubhex2hexaddr(pubkeyhex))) def privhex2addr(privkeyhex,compressed=False): - Msg(bitcoin.privnum2addr(int(privkeyhex,16),compressed)) + Msg(mmb.privnum2addr(int(privkeyhex,16),compressed)) def wif2hex(wif,compressed=False): - Msg(bitcoin.wif2hex(wif)) + Msg(mmb.wif2hex(wif)) def hex2wif(hexpriv,compressed=False): - Msg(bitcoin.hex2wif(hexpriv,compressed)) + Msg(mmb.hex2wif(hexpriv,compressed)) def encrypt(infile,outfile='',hash_preset=''): data = get_data_from_file(infile,'data for encryption',binary=True) diff --git a/mmgen/tx.py b/mmgen/tx.py index bd123fda..358ac672 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -50,7 +50,7 @@ class MMGenTxInput(MMGenListItem): label = MMGenListItemAttr('label','MMGenAddrLabel') class MMGenTxOutput(MMGenListItem): - attrs = 'txid','vout','amt','label','mmid','addr','have_wif' + attrs = 'txid','vout','amt','label','mmid','addr','have_wif','is_chg' label = MMGenListItemAttr('label','MMGenAddrLabel') class MMGenTX(MMGenObject): @@ -71,7 +71,7 @@ class MMGenTX(MMGenObject): self.size = 0 # size of raw serialized tx self.fee = BTCAmt('0') self.send_amt = BTCAmt('0') # total amt minus change - self.hex = '' # raw serialized hex transaction + self.hex = '' # raw serialized hex transaction self.label = MMGenTXLabel('') self.txid = '' self.btc_txid = '' @@ -190,6 +190,9 @@ class MMGenTX(MMGenObject): def get_input_sids(self): return set([e.mmid[:8] for e in self.inputs if e.mmid]) + def get_output_sids(self): + return set([e.mmid[:8] for e in self.outputs if e.mmid]) + def sum_inputs(self): return sum([e.amt for e in self.inputs]) @@ -281,6 +284,8 @@ class MMGenTX(MMGenObject): if self.btc_txid: self.desc = 'sent transaction' msg(m % self.btc_txid.hl()) + self.add_timestamp() + self.add_blockcount(c) return True msg('Sending of transaction {} failed'.format(self.txid)) @@ -339,7 +344,7 @@ class MMGenTX(MMGenObject): out += 'Comment: %s\n%s' % (self.label.hl(),enl) out += 'Inputs:\n' + enl - nonmm_str = '(non-{pnm} address)'.format(pnm=g.proj_name) + nonmm_str = '(non-{pnm} address){s}'.format(pnm=g.proj_name,s=('',' ')[terse]) # for i in self.inputs: print i #DEBUG for n,e in enumerate(self.inputs): if blockcount: @@ -362,15 +367,20 @@ class MMGenTX(MMGenObject): out += 'Outputs:\n' + enl for n,e in enumerate(self.outputs): - mmid_fmt = e.mmid.fmt(width=len(nonmm_str),encl='()',color=True) if e.mmid \ - else MMGenID.hlc(nonmm_str) + if e.mmid: + app=('',' (chg)')[bool(e.is_chg and terse)] + mmid_fmt = e.mmid.fmt(width=len(nonmm_str),encl='()',color=True, + app=app,appcolor='green') + else: + mmid_fmt = MMGenID.hlc(nonmm_str) if terse: out += '%3s: %s %s %s BTC' % (n+1, e.addr.fmt(color=True),mmid_fmt, e.amt.hl()) else: for d in ( (n+1, 'address:', e.addr.fmt(color=True) + ' ' + mmid_fmt), ('', 'comment:', e.label.hl() if e.label else ''), - ('', 'amount:', '%s BTC' % e.amt.hl()) + ('', 'amount:', '%s BTC' % e.amt.hl()), + ('', 'change:', green('True') if e.is_chg else '') ): if d[2]: out += ('%3s %-8s %s\n' % d) out += '\n' diff --git a/mmgen/txcreate.py b/mmgen/txcreate.py index 142ae32b..be0c6fa6 100755 --- a/mmgen/txcreate.py +++ b/mmgen/txcreate.py @@ -109,7 +109,7 @@ def mmaddr2baddr(c,mmaddr,ad_w,ad_f): return BTCAddr(btc_addr) -def get_fee_estimate(): +def get_fee_estimate(c): if 'tx_fee' in opt.set_by_user: # TODO return None else: @@ -175,7 +175,7 @@ def txcreate(opt,cmd_args,do_info=False,caller='txcreate'): if opt.tx_fee > tx.max_fee: die(2,'Transaction fee too large: %s > %s' % (opt.tx_fee,tx.max_fee)) - fee_estimate = get_fee_estimate() + fee_estimate = get_fee_estimate(c) tw = MMGenTrackingWallet(minconf=opt.minconf) tw.view_and_sort() diff --git a/mmgen/txsign.py b/mmgen/txsign.py index aabebde0..1b06ed85 100755 --- a/mmgen/txsign.py +++ b/mmgen/txsign.py @@ -172,7 +172,7 @@ def txsign(opt,c,tx,seed_files,kl,kal,tx_num_str=''): tx.delete_attrs('inputs','have_wif') tx.delete_attrs('outputs','have_wif') - extra_sids = set(saved_seeds) - tx.get_input_sids() + extra_sids = set(saved_seeds) - tx.get_input_sids() - tx.get_output_sids() if extra_sids: msg('Unused Seed ID%s: %s' % (suf(extra_sids,'k'),' '.join(extra_sids))) diff --git a/mmgen/util.py b/mmgen/util.py index e735f2e6..32ddc066 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -384,7 +384,7 @@ def get_seed_file(cmd_args,nargs,invoked_as=None): elif len(cmd_args) > nargs: opts.usage() elif len(cmd_args) == nargs and wf and invoked_as != 'gen': - msg('Warning: overriding wallet in data directory with user-supplied wallet') + msg('Warning: overriding default wallet with user-supplied wallet') if cmd_args or wf: check_infile(cmd_args[0] if cmd_args else wf) @@ -707,6 +707,6 @@ def bitcoin_connection(): g.rpc_user or cfg['rpcuser'], # MMGen's rpcuser,rpcpassword override bitcoind's g.rpc_password or cfg['rpcpassword'], auth_cookie=get_bitcoind_auth_cookie()) - # do an RPC call to make the function fail if we can't connect + # do an RPC call so we exit immediately if we can't connect c.client_version = int(c.getinfo()['version']) return c diff --git a/test/test.py b/test/test.py index a11ba155..c4777ef9 100755 --- a/test/test.py +++ b/test/test.py @@ -80,7 +80,7 @@ ref_tx_label = ''.join([unichr(i) for i in range(65,91) + range(1040,1072) + # cyrillic range(913,939) + # greek range(97,123)])[:MMGenTXLabel.max_len] - +tx_fee = '0.0001' ref_bw_hash_preset = '1' ref_bw_file = 'wallet.mmbrain' ref_bw_file_spc = 'wallet-spaced.mmbrain' @@ -199,8 +199,8 @@ cfgs = { pwfile: 'walletgen', 'mmdat': 'walletgen', 'addrs': 'addrgen', - 'rawtx': 'txcreate', - 'sigtx': 'txsign', + 'rawtx': 'txcreate', + 'sigtx': 'txsign', 'mmwords': 'export_mnemonic', 'mmseed': 'export_seed', 'mmhex': 'export_hex', @@ -990,8 +990,9 @@ def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input='') privnum = getrandnum(32) btcaddr = privnum2addr(privnum,compressed=True) of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn) - write_data_to_file(of, hex2wif('{:064x}'.format(privnum), - compressed=True)+'\n','compressed bitcoin key',silent=True) + wif = hex2wif('{:064x}'.format(privnum),compressed=True) +# Msg(yellow(wif + ' ' + btcaddr)) + write_data_to_file(of,wif+'\n','compressed bitcoin key',silent=True) out.append(create_fake_unspent_entry(btcaddr,non_mmgen='Non-MMGen address'))