Browse Source

mmgen-xmrwallet: new ‘sweep_all’ operation

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.
The MMGen Project 1 year ago
parent
commit
fc7e3c8302
5 changed files with 37 additions and 45 deletions
  1. 1 1
      mmgen/data/version
  2. 8 5
      mmgen/help/xmrwallet.py
  3. 2 2
      mmgen/main_xmrwallet.py
  4. 16 9
      mmgen/xmrwallet.py
  5. 10 28
      test/cmdtest_py_d/ct_xmrwallet.py

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-14.1.dev27
+14.1.dev28

+ 8 - 5
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

+ 2 - 2
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    <TX_file>',
 			'[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]

+ 16 - 9
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))
 

+ 10 - 28
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')
-		min_bal = XMRAmt('0.9')
+		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}')
 
-		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
+		min_bal = XMRAmt('0.9')
+		return await self.mine_chk('alice', 3, 0, lambda x: x.ub > min_bal, f'bal > {min_bal}')
 
 	# wallet methods