From fc7e3c8302ac73d6f31d9aee32896429139dbc3d Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 10 Apr 2024 09:05:51 +0000 Subject: [PATCH] =?UTF-8?q?mmgen-xmrwallet:=20new=20=E2=80=98sweep=5Fall?= =?UTF-8?q?=E2=80=99=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This operation sweeps balances of all addresses in an account, as opposed to ‘sweep’, which sweeps the balance of a single randomly chosen address. For accounts having only one address with a balance, the two operations are identical. --- mmgen/data/version | 2 +- mmgen/help/xmrwallet.py | 13 +++++++---- mmgen/main_xmrwallet.py | 4 ++-- mmgen/xmrwallet.py | 25 ++++++++++++-------- test/cmdtest_py_d/ct_xmrwallet.py | 38 ++++++++----------------------- 5 files changed, 37 insertions(+), 45 deletions(-) diff --git a/mmgen/data/version b/mmgen/data/version index 55d32778..345bfdd4 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -14.1.dev27 +14.1.dev28 diff --git a/mmgen/help/xmrwallet.py b/mmgen/help/xmrwallet.py index 0fbe77ae..3cc19f85 100755 --- a/mmgen/help/xmrwallet.py +++ b/mmgen/help/xmrwallet.py @@ -48,7 +48,8 @@ new - create a new account in a wallet, or a new address in an account transfer - transfer specified XMR amount from specified wallet:account to specified address sweep - sweep funds in specified wallet:account to new address in same - account or new account in another wallet + account, or new or specified account in another wallet +sweep_all - same as above, but sweep balances of all addresses in the account relay - relay a transaction from a transaction file created using ‘sweep’ or ‘transfer’ with the --no-relay option submit - submit an autosigned transaction to a wallet and the network @@ -102,9 +103,10 @@ where SOURCE is a wallet number, ACCOUNT the source account index, ADDRESS the destination Monero address and AMOUNT the XMR amount to be sent. - ‘SWEEP’ OPERATION NOTES + ‘SWEEP’ AND ‘SWEEP_ALL’ OPERATION NOTES -The sweep operation takes a SWEEP_SPEC arg with the following format: +The sweep and sweep_all operations take a SWEEP_SPEC arg with the following +format: {sweep_spec} @@ -123,8 +125,9 @@ swept into ACCOUNT of DEST. The user is prompted before addresses are created or funds transferred. -Note that multiple sweep operations may be required to sweep all the funds -in an account. +With ‘sweep’, if the source account has more than one address with a balance, +the balance of a single randomly chosen address will be swept. To sweep the +balances of all addresses in an account, use ‘sweep_all’. ‘SUBMIT’ AND ‘RELAY’ OPERATION NOTES diff --git a/mmgen/main_xmrwallet.py b/mmgen/main_xmrwallet.py index 23d88489..5eced033 100755 --- a/mmgen/main_xmrwallet.py +++ b/mmgen/main_xmrwallet.py @@ -45,7 +45,7 @@ opts_data = { '[opts] label [xmr_keyaddrfile] LABEL_SPEC', '[opts] new [xmr_keyaddrfile] NEW_ADDRESS_SPEC', '[opts] transfer [xmr_keyaddrfile] TRANSFER_SPEC', - '[opts] sweep [xmr_keyaddrfile] SWEEP_SPEC', + '[opts] sweep | sweep_all [xmr_keyaddrfile] SWEEP_SPEC', '[opts] submit [TX_file]', '[opts] relay ', '[opts] resubmit | abort (for use with --autosign only)', @@ -139,7 +139,7 @@ elif op in ('create','sync','list','view','listview','dump','restore'): # kafile if len(cmd_args) > 1: cfg._opts.usage() wallets = cmd_args.pop(0) if cmd_args else None -elif op in ('new','transfer','sweep','label'): +elif op in ('new', 'transfer', 'sweep', 'sweep_all', 'label'): if len(cmd_args) != 1: cfg._opts.usage() spec = cmd_args[0] diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index aa399646..1fad3d8a 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -217,7 +217,7 @@ class MoneroMMGenTX: 'amount', 'fee', 'blob' } - oneline_fs = '{a:7} {b:8} {c:19} {d:13} {e:8} {f:6} {x:2} {g:6} {h:17} {j}' + oneline_fs = '{a:7} {b:8} {c:19} {d:13} {e:9} {f:6} {x:2} {g:6} {h:17} {j}' chksum_nchars = 6 xmrwallet_tx_data = namedtuple('xmrwallet_tx_data',[ 'op', @@ -254,7 +254,7 @@ class MoneroMMGenTX: b = d.seed_id.hl(), c = make_timestr(d.submit_time if d.submit_time is not None else d.create_time), d = orange(self.file_id), - e = purple(capfirst(d.op.ljust(8))), + e = purple(d.op.replace('_', ' ').title().ljust(9)), f = red('{}:{}'.format(d.source.wallet,d.source.account).ljust(6)), g = red('{}:{}'.format(d.dest.wallet,d.dest.account).ljust(6)) if d.dest else cyan('ext '), h = d.amount.fmt( color=True, iwidth=4, prec=12 ), @@ -640,6 +640,7 @@ class MoneroWalletOps: 'new', 'transfer', 'sweep', + 'sweep_all', 'relay', 'txview', 'txlist', @@ -663,6 +664,7 @@ class MoneroWalletOps: 'new', 'transfer', 'sweep', + 'sweep_all', 'dump', 'restore' ) @@ -1138,11 +1140,13 @@ class MoneroWalletOps: unsigned_txset = res['unsigned_txset'] if self.cfg.watch_only else None, ) - def make_sweep_tx(self,account,dest_acct,dest_addr_idx,addr): + def make_sweep_tx(self, account, dest_acct, dest_addr_idx, addr, addrs_data): res = self.c.call( 'sweep_all', address = addr, account_index = account, + subaddr_indices = list(range(len(addrs_data[account]['addresses']))) + if self.parent.name == 'sweep_all' else [], priority = self.cfg.priority or None, do_not_relay = True, get_tx_hex = True, @@ -1546,7 +1550,7 @@ class MoneroWalletOps: else: return s # None or empty string - if self.name == 'sweep': + if self.name in ('sweep', 'sweep_all'): self.dest_acct = None if m[4] is None else int(m[4]) elif self.name == 'transfer': self.dest_addr = CoinAddr(self.proto,m[3]) @@ -1592,7 +1596,7 @@ class MoneroWalletOps: cfg = self.cfg, daemon = wd2 ) - def create_tx(self, h, accts_data): + def create_tx(self, h, accts_data, addrs_data): def create_new_addr_maybe(h, account, label=None): if keypress_confirm(self.cfg, f'\nCreate new address for account #{account}?'): @@ -1657,7 +1661,7 @@ class MoneroWalletOps: assert dest_addr_chk in (None, dest_addr), 'dest_addr_chk' msg(f'\n Creating {self.name} transaction...') - return (h, h.make_sweep_tx(self.account, dest_acct, dest_addr_idx, dest_addr)) + return (h, h.make_sweep_tx(self.account, dest_acct, dest_addr_idx, dest_addr, addrs_data)) @property def add_desc(self): @@ -1682,13 +1686,13 @@ class MoneroWalletOps: h = self.rpc(self,self.source) h.open_wallet('source') - accts_data = h.get_accts()[0] + accts_data, addrs_data = h.get_accts() self.check_account_exists(accts_data, self.account) h.print_addrs(accts_data,self.account) - h, new_tx = self.create_tx(h, accts_data) + h, new_tx = self.create_tx(h, accts_data, addrs_data) msg('\n' + new_tx.get_info(indent=' ')) @@ -1715,6 +1719,9 @@ class MoneroWalletOps: else: die(1,'\nExiting at user request') + class sweep_all(sweep): + stem = 'sweep' + class transfer(sweep): stem = 'transferr' spec_id = 'transfer_spec' @@ -1724,7 +1731,7 @@ class MoneroWalletOps: def add_desc(self): return f': {self.amount} XMR to {self.dest_addr}' - def create_tx(self, h, accts_data): + def create_tx(self, h, accts_data, addrs_data): msg(f'\n Creating {self.name} transaction...') return (h, h.make_transfer_tx(self.account, self.dest_addr, self.amount)) diff --git a/test/cmdtest_py_d/ct_xmrwallet.py b/test/cmdtest_py_d/ct_xmrwallet.py index 9c7abdbb..76a7daec 100755 --- a/test/cmdtest_py_d/ct_xmrwallet.py +++ b/test/cmdtest_py_d/ct_xmrwallet.py @@ -528,7 +528,6 @@ class CmdTestXMRWallet(CmdTestBase): arg2, tx_relay_parm = None, no_relay = False, - return_amt = False, use_existing = False, add_opts = [], add_desc = None, @@ -558,7 +557,7 @@ class CmdTestXMRWallet(CmdTestBase): if op == 'sign': return t - if op == 'sweep': + if op in ('sweep', 'sweep_all'): desc = 'address' if re.match(r'.*:\d+$', arg2) else 'account' t.expect( rf'Create new {desc} .* \(y/N\): ', @@ -567,9 +566,6 @@ class CmdTestXMRWallet(CmdTestBase): if use_existing: t.expect(rf'to last existing {desc} .* \(y/N\): ', 'y', regex=True) - if return_amt: - amt = XMRAmt(strip_ansi_escapes(t.expect_getend('Amount: ')).replace('XMR','').strip()) - dtype = 'unsigned' if data.autosign else 'signed' t.expect(f'Save {dtype} transaction? (y/N): ','y') t.written_to_file(f'{dtype.capitalize()} transaction') @@ -580,7 +576,7 @@ class CmdTestXMRWallet(CmdTestBase): t.read() - return t if do_ret else amt if return_amt else t.ok() + return t if do_ret else t.ok() def sweep_to_wallet(self): self.do_op('sweep', 'alice', '1:0,2') @@ -657,29 +653,15 @@ class CmdTestXMRWallet(CmdTestBase): return await self.mine_chk('alice',2,1,lambda x: x.ub > 0.9,'unlocked balance > 0.9') async def sweep_create_and_send(self): - bal = XMRAmt('0') + get_file_with_ext(self.users['alice'].udir, 'sigtx', delete_all=True) + + self.do_op('sweep_all', 'alice', '2:1,3', no_relay=True, use_existing=True) + ok() + + self.relay_tx(f'--tx-relay-daemon={self.tx_relay_daemon_parm}') + min_bal = XMRAmt('0.9') - - for i in range(4): - if i: - ok() - get_file_with_ext(self.users['alice'].udir,'sigtx',delete_all=True) - send_amt = self.do_op( - 'sweep','alice','2:1,3', # '2:1,3' - no_relay = True, - use_existing = True, - add_desc = f'TX #{i+1}', - return_amt = True ) - ok() - self.relay_tx(f'--tx-relay-daemon={self.tx_relay_daemon_parm}',add_desc=f'send amt: {send_amt} XMR') - await self.mine_chk('alice',2,1,lambda x: 'chk_bal_chg','balance has changed') - ok() - bal_info = await self.mine_chk('alice',3,0,lambda x,y=bal: x.ub > y, f'bal > {bal}',return_bal=True) - bal += bal_info.ub - if bal >= min_bal: - return 'ok' - - return False + return await self.mine_chk('alice', 3, 0, lambda x: x.ub > min_bal, f'bal > {min_bal}') # wallet methods