From 9412505231ab3de5bd9a081aea1fe35cd5cdf3e6 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 30 Jan 2023 10:11:21 +0000 Subject: [PATCH] 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. --- mmgen/data/mmgen.cfg | 5 +++++ mmgen/data/version | 2 +- mmgen/globalvars.py | 3 +++ mmgen/main_txcreate.py | 1 + mmgen/main_txdo.py | 1 + mmgen/tw/addresses.py | 28 +++++++++++++++++++++++----- test/test_py_d/ts_regtest.py | 35 ++++++++++++++++++++++++++++++++++- 7 files changed, 68 insertions(+), 7 deletions(-) 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',