Browse Source

mmgen-{txcreate,txdo}: new `--autochg-ignore-labels` option

As of this commit, addresses in the tracking wallet with labels are considered
reserved, i.e. equivalent to used, for purposes of automatic change address
selection.

Use of this option restores the former default behavior of ignoring labels
during automatic change address selection, with one small difference: when
an address with a label is chosen as a change address candidate, a warning
is displayed before prompting the user for confirmation.

The option may also be set in the configuration file.
The MMGen Project 2 years ago
parent
commit
9412505231

+ 5 - 0
mmgen/data/mmgen.cfg

@@ -79,6 +79,11 @@
 # also turns off all information output for the configured wordlists:
 # mnemonic_entry_modes mmgen:minimal bip39:fixed xmrseed:short
 
+# Uncomment to allow addresses with labels to be used as change addresses.
+# This option is meaningful only for automatic change address selection.
+# When change addresses are chosen manually the option is ignored:
+# autochg_ignore_labels true
+
 ############################
 ## Ignore daemon versions ##
 ############################

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.3.dev35
+13.3.dev36

+ 3 - 0
mmgen/globalvars.py

@@ -86,6 +86,7 @@ class GlobalContext(Lockable):
 	testnet              = False
 	regtest              = False
 	accept_defaults      = False
+	autochg_ignore_labels = False
 
 	# rpc:
 	rpc_host             = ''
@@ -152,6 +153,7 @@ class GlobalContext(Lockable):
 
 	# global var sets user opt:
 	global_sets_opt = (
+		'autochg_ignore_labels',
 		'debug',
 		'minconf',
 		'quiet',
@@ -198,6 +200,7 @@ class GlobalContext(Lockable):
 		('tx_id','terse_info'),
 	)
 	cfg_file_opts = (
+		'autochg_ignore_labels',
 		'color',
 		'daemon_data_dir',
 		'debug',

+ 1 - 0
mmgen/main_txcreate.py

@@ -50,6 +50,7 @@ opts_data = {
                       MMGen IDs or coin addresses).  Note that ALL unspent
                       outputs associated with each address will be included.
 -l, --locktime=    t  Lock time (block height or unix seconds) (default: 0)
+-L, --autochg-ignore-labels Ignore labels when autoselecting change addresses
 -m, --minconf=     n  Minimum number of confirmations required to spend
                       outputs (default: 1)
 -q, --quiet           Suppress warnings; overwrite files without prompting

+ 1 - 0
mmgen/main_txdo.py

@@ -62,6 +62,7 @@ opts_data = {
 -K, --keygen-backend=n Use backend 'n' for public key generation.  Options
                        for {coin_id}: {kgs}
 -l, --locktime=      t Lock time (block height or unix seconds) (default: 0)
+-L, --autochg-ignore-labels Ignore labels when autoselecting change addresses
 -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-

+ 23 - 5
mmgen/tw/addresses.py

@@ -16,7 +16,7 @@ from ..util import msg,suf,is_int
 from ..objmethods import MMGenObject
 from ..obj import MMGenListItem,ImmutableAttr,ListItemAttr,TwComment,NonNegativeInt
 from ..addr import CoinAddr,MMGenID,MMGenAddrType
-from ..color import red,green
+from ..color import red,green,yellow
 from .view import TwView
 from .shared import TwMMGenID
 
@@ -320,9 +320,18 @@ class TwAddresses(TwView):
 			top = len(data) - 1 if top is None else top )
 
 		if start is not None:
+			from ..opts import opt
 			for d in data[start:]:
 				if d.al_id == al_id:
-					if not d.recvd:
+					if not d.recvd and (opt.autochg_ignore_labels or not d.comment):
+						if d.comment:
+							msg('{} {} {} {}{}'.format(
+								yellow('WARNING: address'),
+								d.twmmid.hl(),
+								yellow('has a label,'),
+								d.comment.hl2(encl='‘’'),
+								yellow(',\n  but allowing it for change anyway by user request')
+							))
 						return d
 				else:
 					break
@@ -339,10 +348,19 @@ class TwAddresses(TwView):
 		"""
 
 		def choose_address(addrs):
-			from ..ui import line_input
-			prompt = '\nChoose a change address:\n\n{m}\n\nEnter a number> '.format(
-				m = '\n'.join(f'{n:3}) {a.twmmid.hl()}' for n,a in enumerate(addrs,1))
+
+			def format_line(n,d):
+				return '{a:3}) {b}{c}'.format(
+					a = n,
+					b = d.twmmid.hl(),
+					c = yellow(' <== has a label!') if d.comment else ''
+				)
+
+			prompt = '\nChoose a change address:\n\n{}\n\nEnter a number> '.format(
+				'\n'.join(format_line(n,d) for n,d in enumerate(addrs,1))
 			)
+
+			from ..ui import line_input
 			while True:
 				res = line_input(prompt)
 				if is_int(res) and 0 < int(res) <= len(addrs):

+ 34 - 1
test/test_py_d/ts_regtest.py

@@ -386,6 +386,12 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		('bob_auto_chg_addrtype2', 'creating an automatic change address transaction by addrtype (B)'),
 		('bob_auto_chg_addrtype3', 'creating an automatic change address transaction by addrtype (S)'),
 		('bob_auto_chg_addrtype4', 'creating an automatic change address transaction by addrtype (single address)'),
+		('bob_add_comment_uua1',   'adding a comment for unused address in tracking wallet (C)'),
+		('bob_auto_chg5',          'creating an auto-chg-address TX, skipping unused address with label (C)'),
+		('bob_auto_chg_addrtype5', 'creating an auto-chg-address TX by addrtype, skipping unused address with label (C)'),
+		('bob_auto_chg6',          'creating an auto-chg-address TX, using unused address with label (C)'),
+		('bob_auto_chg_addrtype6', 'creating an auto-chg-address TX by addrtype, using unused address with label (C)'),
+		('bob_remove_comment_uua1', 'removing a comment for unused address in tracking wallet (C)'),
 		('bob_auto_chg_bad1', 'error handling for auto change address transaction (bad ID FFFFFFFF:C)'),
 		('bob_auto_chg_bad2', 'error handling for auto change address transaction (bad ID 00000000:C)'),
 		('bob_auto_chg_bad3', 'error handling for auto change address transaction (no unused addresses)'),
@@ -1652,13 +1658,14 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 			return 'skip'
 		return self.generate()
 
-	def _usr_auto_chg(self,user,mmtype,idx,by_mmtype=False,include_dest=True,by_addrtype=False):
+	def _usr_auto_chg(self,user,mmtype,idx,by_mmtype=False,include_dest=True,by_addrtype=False,ignore_labels=False):
 		if mmtype in ('S','B') and not self.proto.cap('segwit'):
 			return 'skip'
 		sid = self._user_sid('bob')
 		t = self.spawn(
 			'mmgen-txcreate',
 				[f'--outdir={self.tr.trash_dir}', '--no-blank', f'--{user}'] +
+				(['--autochg-ignore-labels'] if ignore_labels else []) +
 				[mmtype if by_mmtype else f'{sid}:{mmtype}'] +
 				([self.burn_addr+',0.01'] if include_dest else [])
 			)
@@ -1692,6 +1699,32 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def bob_auto_chg_addrtype4(self):
 		return self._usr_auto_chg( 'bob', 'C', '3', True, include_dest=False )
 
+	def _bob_add_comment_uua(self,addrspec,comment):
+		sid = self._user_sid('bob')
+		return self.user_add_comment('bob',sid+addrspec,comment)
+
+	def bob_add_comment_uua1(self):
+		return self._bob_add_comment_uua(':C:3','comment for unused address')
+
+	def bob_auto_chg5(self):
+		return self._usr_auto_chg( 'bob', 'C', '4' )
+
+	def bob_auto_chg_addrtype5(self):
+		return self._usr_auto_chg( 'bob', 'C', '4', True )
+
+	def bob_auto_chg6(self):
+		return self._usr_auto_chg( 'bob', 'C', '3', ignore_labels=True )
+
+	def bob_auto_chg_addrtype6(self):
+		return self._usr_auto_chg( 'bob', 'C', '3', True, ignore_labels=True )
+
+	def _bob_remove_comment_uua(self,addrspec):
+		sid = self._user_sid('bob')
+		return self.user_remove_comment('bob',sid+addrspec)
+
+	def bob_remove_comment_uua1(self):
+		return self._bob_remove_comment_uua(':C:3')
+
 	def _usr_auto_chg_bad(self,user,al_id,expect):
 		t = self.spawn(
 			'mmgen-txcreate',