diff --git a/mmgen/data/version b/mmgen/data/version index b8bb4137..8fc91924 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -15.1.dev36 +15.1.dev37 diff --git a/mmgen/help/help_notes.py b/mmgen/help/help_notes.py index 54c55939..0463ee85 100755 --- a/mmgen/help/help_notes.py +++ b/mmgen/help/help_notes.py @@ -35,6 +35,10 @@ class help_notes: pfx, sfx, sep, conj = (('', '', ',', ' or '), ("‘", "’", "’,‘", "’ or ‘"))[use_quotes] return pfx + sep.join(u[0] for u in cu[:-1]) + ('', conj)[len(cu)>1] + cu[-1][0] + sfx + def stream_interval(self): + from ..tx.new_swap import get_swap_proto_mod + return get_swap_proto_mod(self.cfg.swap_proto).SwapCfg(self.cfg).si.dfl + def fee_spec_names(self, *, proto=None, linebreak=' '): cu = (proto or self.proto).coin_amt.units return ( diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index c6338651..8cfd7b7c 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -73,6 +73,7 @@ opts_data = { -- -m, --minconf= n Minimum number of confirmations required to spend + outputs (default: 1) -- -q, --quiet Suppress warnings; overwrite files without prompting + -s -r, --stream-interval=N Set block interval for streaming swap (default: {si}) bt -R, --no-rbf Make transaction non-replaceable (non-replace-by-fee + according to BIP 125) -s -s, --swap-proto Swap protocol to use (Default: {x_dfl}, @@ -97,6 +98,7 @@ opts_data = { a_info = help_notes('account_info_desc'), fu = help_notes('rel_fee_desc'), fl = help_notes('fee_spec_letters'), + si = help_notes('stream_interval'), fe_all = fmt_list(cfg._autoset_opts['fee_estimate_mode'].choices, fmt='no_spc'), fe_dfl = cfg._autoset_opts['fee_estimate_mode'].choices[0], x_all = fmt_list(cfg._autoset_opts['swap_proto'].choices, fmt='no_spc'), diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index 6b9d63d6..e91b4354 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -94,6 +94,7 @@ opts_data = { + for password hashing (default: '{gc.dfl_hash_preset}') -- -P, --passwd-file= f Get {pnm} wallet passphrase from file 'f' -- -q, --quiet Suppress warnings; overwrite files without prompting + -s -r, --stream-interval=N Set block interval for streaming swap (default: {si}) bt -R, --no-rbf Make transaction non-replaceable (non-replace-by-fee + according to BIP 125) -s -s, --swap-proto Swap protocol to use (Default: {x_dfl}, @@ -145,6 +146,7 @@ column below: fl = help_notes('fee_spec_letters'), dsl = help_notes('dfl_seed_len'), ss = help_notes('dfl_subseeds'), + si = help_notes('stream_interval'), tx_proxies = help_notes('tx_proxies'), ss_max = SubSeedIdxRange.max_idx, fe_all = fmt_list(cfg._autoset_opts['fee_estimate_mode'].choices, fmt='no_spc'), diff --git a/mmgen/swap/cfg.py b/mmgen/swap/cfg.py index 3680bbff..18f339ed 100644 --- a/mmgen/swap/cfg.py +++ b/mmgen/swap/cfg.py @@ -13,10 +13,13 @@ swap.cfg: swap configuration class the MMGen Wallet suite """ import re +from collections import namedtuple from ..amt import UniAmt from ..util import die +_mmd = namedtuple('swap_config_option', ['min', 'max', 'dfl']) + class SwapCfg: # The trade limit, i.e., set 100000000 to get a minimum of 1 full asset, else a refund @@ -24,7 +27,7 @@ class SwapCfg: trade_limit = None # Swap interval for streaming swap in blocks. Optional. If 0, do not stream - stream_interval = 3 + si = _mmd(1, 20, 3) # stream_interval # Swap quantity for streaming swap. # The interval value determines the frequency of swaps in blocks @@ -38,9 +41,25 @@ class SwapCfg: if cfg.trade_limit is not None: self.set_trade_limit(desc='parameter for --trade-limit') + if cfg.stream_interval is None: + self.stream_interval = self.si.dfl + else: + self.set_stream_interval(desc='parameter for --stream-interval') + def set_trade_limit(self, *, desc): s = self.cfg.trade_limit if re.match(r'-*[0-9]+(\.[0-9]+)*%*$', s): self.trade_limit = 1 - float(s[:-1]) / 100 if s.endswith('%') else UniAmt(s) else: die('SwapCfgValueError', f'{s}: invalid {desc}') + + def set_stream_interval(self, *, desc): + s = self.cfg.stream_interval + from ..util import is_int + if not is_int(s): + die('SwapCfgValueError', f'{s}: invalid {desc} (not an integer)') + self.stream_interval = si = int(s) + if si < self.si.min: + die('SwapCfgValueError', f'{si}: invalid {desc} (< {self.si.min})') + if si > self.si.max: + die('SwapCfgValueError', f'{si}: invalid {desc} (> {self.si.max})') diff --git a/test/cmdtest_d/ethswap.py b/test/cmdtest_d/ethswap.py index e0fe1803..6401d3f6 100755 --- a/test/cmdtest_d/ethswap.py +++ b/test/cmdtest_d/ethswap.py @@ -415,8 +415,8 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev return self._swaptxcreate_ui_common( self._swaptxcreate( ['ETH', '8.765', 'BTC', f'{dfl_sid}:B:4'], - add_opts = ['--trade-limit=3%']), - expect = ':2019e4/3/0') + add_opts = ['--trade-limit=3%' ,'--stream-interval=7']), + expect = ':2019e4/7/0') def swaptxcreate3a(self): t = self._swaptxcreate(['ETH', '0.7654321', 'ETH.MM1'], add_opts=['--gas=fallback']) diff --git a/test/cmdtest_d/swap.py b/test/cmdtest_d/swap.py index 1ad773a1..a1a598ee 100755 --- a/test/cmdtest_d/swap.py +++ b/test/cmdtest_d/swap.py @@ -534,16 +534,16 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded): return self._swaptxcreate_ui_common( self._swaptxcreate( ['BCH', '1.234', f'{self.sid}:C:{idx}', 'LTC', f'{self.sid}:B:3'], - add_opts = ['--trade-limit=0%']), - expect = ':3541e5/3/0') + add_opts = ['--trade-limit=0%', '--stream-interval=1']), + expect = ':3541e5/1/0') def swaptxcreate2(self): t = self._swaptxcreate( ['BCH', 'LTC'], - add_opts = ['--no-quiet', '--trade-limit=3.337%']) + add_opts = ['--no-quiet', '--stream-interval=10', '--trade-limit=3.337%']) t.expect('Enter a number> ', '1') t.expect('OK? (Y/n): ', 'y') - return self._swaptxcreate_ui_common(t, reload_quote=True, expect=':1386e6/3/0') + return self._swaptxcreate_ui_common(t, reload_quote=True, expect=':1386e6/10/0') def swaptxcreate3(self): return self._swaptxcreate_ui_common( diff --git a/test/modtest_d/swap.py b/test/modtest_d/swap.py index 704e3d2d..d0cb8bdf 100755 --- a/test/modtest_d/swap.py +++ b/test/modtest_d/swap.py @@ -16,20 +16,23 @@ class unit_tests: def cfg(self, name, ut, desc='Swap configuration'): - for tl_arg, tl_chk in ( - (None, None), - ('1', UniAmt('1')), - ('33', UniAmt('33')), - ('2%', 0.98), - ('-2%', 1.02), - ('3.333%', 0.96667), - ('-3.333%', 1.03333), - ('1.2345', UniAmt('1.2345'))): - cfg_data = {'trade_limit': tl_arg} + for tl_arg, tl_chk, si_arg in ( + (None, None, None), + ('1', UniAmt('1'), None), + ('33', UniAmt('33'), 7), + ('2%', 0.98, 14), + ('-2%', 1.02, 1), + ('3.333%', 0.96667, 1), + ('-3.333%', 1.03333, 3), + ('1.2345', UniAmt('1.2345'), 10)): + cfg_data = { + 'trade_limit': tl_arg, + 'stream_interval': None if si_arg is None else str(si_arg)} sc = SwapCfg(Config(cfg_data)) vmsg(f' trade_limit: {tl_arg} => {sc.trade_limit}') + vmsg(f' stream_interval: {si_arg} => {sc.stream_interval}') assert sc.trade_limit == tl_chk - assert sc.stream_interval == 3 + assert sc.stream_interval == sc.si.dfl if si_arg is None else si_arg assert sc.stream_quantity == 0 vmsg('\n Testing error handling') @@ -40,9 +43,21 @@ class unit_tests: def bad2(): SwapCfg(Config({'trade_limit': '1.23x'})) + def bad3(): + SwapCfg(Config({'stream_interval': 30})) + + def bad4(): + SwapCfg(Config({'stream_interval': 0})) + + def bad5(): + SwapCfg(Config({'stream_interval': 'x'})) + ut.process_bad_data(( ('bad1', 'SwapCfgValueError', 'invalid parameter', bad1), ('bad2', 'SwapCfgValueError', 'invalid parameter', bad2), + ('bad3', 'SwapCfgValueError', 'invalid parameter', bad3), + ('bad4', 'SwapCfgValueError', 'invalid parameter', bad4), + ('bad5', 'SwapCfgValueError', 'invalid parameter', bad5), ), pfx='') return True @@ -78,15 +93,15 @@ class unit_tests: vmsg(f'\nTesting asset {cyan(asset_name)}:') - for limit, limit_chk, suf in ( - ('123.4567', 12340000000, '1234e7/3/0'), - ('1.234567', 123400000, '1234e5/3/0'), - ('0.01234567', 1234000, '1234e3/3/0'), - ('0.00012345', 12345, '12345/3/0'), - (None, 0, '0/3/0'), + for limit, limit_chk, si, suf in ( + ('123.4567', 12340000000, None, '1234e7/3/0'), + ('1.234567', 123400000, 1, '1234e5/1/0'), + ('0.01234567', 1234000, 10, '1234e3/10/0'), + ('0.00012345', 12345, 20, '12345/20/0'), + (None, 0, 3, '0/3/0'), ): vmsg('\nTesting memo initialization:') - swap_cfg = SwapCfg(Config({'trade_limit': limit})) + swap_cfg = SwapCfg(Config({'trade_limit': limit, 'stream_interval': si})) m = Memo( swap_cfg, proto, @@ -112,7 +127,7 @@ class unit_tests: assert p.asset == token or coin.upper() assert p.address == addr.views[addr.view_pref] assert p.trade_limit == limit_chk - assert p.stream_interval == 3 + assert p.stream_interval == si or swap_cfg.si.dfl, f'{p.stream_interval} != {swap_cfg.si.dfl}' assert p.stream_quantity == 0 # auto vmsg('\nTesting is_partial_memo():')