diff --git a/mmgen/data/mmgen.cfg b/mmgen/data/mmgen.cfg index bea833d0..f6b0d97a 100644 --- a/mmgen/data/mmgen.cfg +++ b/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 ## ############################ diff --git a/mmgen/data/version b/mmgen/data/version index d1fce55b..8291c9ee 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -13.3.dev35 +13.3.dev36 diff --git a/mmgen/globalvars.py b/mmgen/globalvars.py index 62ca6533..0d298932 100755 --- a/mmgen/globalvars.py +++ b/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', diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index aeb7a3d4..dc8de7ea 100755 --- a/mmgen/main_txcreate.py +++ b/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 diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index bcda79a6..02f8edc5 100755 --- a/mmgen/main_txdo.py +++ b/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- diff --git a/mmgen/tw/addresses.py b/mmgen/tw/addresses.py index c099612f..84b9f596 100755 --- a/mmgen/tw/addresses.py +++ b/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): diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index 91c1d185..b40cacc4 100755 --- a/test/test_py_d/ts_regtest.py +++ b/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',