Browse Source

Pre-RBF changes, bugfixes

philemon 7 years ago
parent
commit
8556de2101
12 changed files with 84 additions and 63 deletions
  1. 5 6
      mmgen/main_addrimport.py
  2. 1 1
      mmgen/main_txcreate.py
  3. 7 5
      mmgen/main_txdo.py
  4. 11 10
      mmgen/main_wallet.py
  5. 7 2
      mmgen/obj.py
  6. 1 1
      mmgen/test.py
  7. 25 22
      mmgen/tool.py
  8. 16 6
      mmgen/tx.py
  9. 2 2
      mmgen/txcreate.py
  10. 1 1
      mmgen/txsign.py
  11. 2 2
      mmgen/util.py
  12. 6 5
      test/test.py

+ 5 - 6
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')

+ 1 - 1
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),

+ 7 - 5
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

+ 11 - 10
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'))

+ 7 - 2
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

+ 1 - 1
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

+ 25 - 22
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)

+ 16 - 6
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'

+ 2 - 2
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()

+ 1 - 1
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)))

+ 2 - 2
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

+ 6 - 5
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'))