Browse Source

mmgen-xmrwallet: support sweeping to specific account of wallet

    old sweep_spec: SOURCE:ACCOUNT[,DEST]

    new sweep_spec: SOURCE:ACCOUNT[,DEST[:ACCOUNT]]

    If DEST is included without its related ACCOUNT, funds from ACCOUNT of
    SOURCE will be swept into a newly created account in DEST, or the last
    existing account of DEST, if requested by the user.

    If both account indices are included, funds from ACCOUNT of SOURCE will
    be swept into ACCOUNT of DEST.

Testing:

    $ test/cmdtest.py --coin=xmr -e -X sweep_to_wallet_account_proxy xmrwallet
The MMGen Project 11 months ago
parent
commit
f39da52b
4 changed files with 63 additions and 20 deletions
  1. 1 1
      mmgen/data/version
  2. 8 4
      mmgen/help/xmrwallet.py
  3. 33 4
      mmgen/xmrwallet.py
  4. 21 11
      test/cmdtest_py_d/ct_xmrwallet.py

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-14.1.dev26
+14.1.dev27

+ 8 - 4
mmgen/help/xmrwallet.py

@@ -108,14 +108,18 @@ The sweep operation takes a SWEEP_SPEC arg with the following format:
 
 
     {sweep_spec}
     {sweep_spec}
 
 
-where SOURCE and DEST are wallet numbers and ACCOUNT an account index.
+where SOURCE and DEST are wallet numbers and ACCOUNT account indices for the
+respective wallets.
 
 
 If DEST is omitted, a new address will be created in ACCOUNT of SOURCE, and
 If DEST is omitted, a new address will be created in ACCOUNT of SOURCE, and
 funds from ACCOUNT of SOURCE will be swept into it.
 funds from ACCOUNT of SOURCE will be swept into it.
 
 
-If DEST is included, all funds from ACCOUNT of SOURCE will be swept into a
+If DEST is included without its related ACCOUNT, funds from ACCOUNT of SOURCE
-newly created account in DEST, or the last existing account, if requested
+will be swept into a newly created account in DEST, or the last existing
-by the user.
+account of DEST, if requested by the user.
+
+If both account indices are included, funds from ACCOUNT of SOURCE will be
+swept into ACCOUNT of DEST.
 
 
 The user is prompted before addresses are created or funds transferred.
 The user is prompted before addresses are created or funds transferred.
 
 

+ 33 - 4
mmgen/xmrwallet.py

@@ -70,7 +70,7 @@ xmrwallet_uarg_info = (
 		'tx_relay_daemon': e('HOST:PORT[:PROXY_IP:PROXY_PORT]',     rf'({hp})(?::({hp}))?'),
 		'tx_relay_daemon': e('HOST:PORT[:PROXY_IP:PROXY_PORT]',     rf'({hp})(?::({hp}))?'),
 		'newaddr_spec':    e('WALLET[:ACCOUNT][,"label text"]',     r'(\d+)(?::(\d+))?(?:,(.*))?'),
 		'newaddr_spec':    e('WALLET[:ACCOUNT][,"label text"]',     r'(\d+)(?::(\d+))?(?:,(.*))?'),
 		'transfer_spec':   e('SOURCE:ACCOUNT:ADDRESS,AMOUNT',       rf'(\d+):(\d+):([{b58a}]+),([0-9.]+)'),
 		'transfer_spec':   e('SOURCE:ACCOUNT:ADDRESS,AMOUNT',       rf'(\d+):(\d+):([{b58a}]+),([0-9.]+)'),
-		'sweep_spec':      e('SOURCE:ACCOUNT[,DEST]',               r'(\d+):(\d+)(?:,(\d+))?'),
+		'sweep_spec':      e('SOURCE:ACCOUNT[,DEST[:ACCOUNT]]',     r'(\d+):(\d+)(?:,(\d+)(?::(\d+))?)?'),
 		'label_spec':      e('WALLET:ACCOUNT:ADDRESS,"label text"', r'(\d+):(\d+):(\d+),(.*)'),
 		'label_spec':      e('WALLET:ACCOUNT:ADDRESS,"label text"', r'(\d+):(\d+):(\d+),(.*)'),
 	})(
 	})(
 		namedtuple('uarg_info_entry',['annot','pat']),
 		namedtuple('uarg_info_entry',['annot','pat']),
@@ -1517,7 +1517,9 @@ class MoneroWalletOps:
 				else:
 				else:
 					return s # None or empty string
 					return s # None or empty string
 
 
-			if self.name == 'transfer':
+			if self.name == 'sweep':
+				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])
 				self.dest_addr = CoinAddr(self.proto,m[3])
 				self.amount = self.proto.coin_amt(m[4])
 				self.amount = self.proto.coin_amt(m[4])
 			elif self.name == 'new':
 			elif self.name == 'new':
@@ -1577,7 +1579,7 @@ class MoneroWalletOps:
 				dest_addr_chk = create_new_addr_maybe(h, self.account)
 				dest_addr_chk = create_new_addr_maybe(h, self.account)
 				dest_addr, dest_addr_idx = h.get_last_addr(self.account, display=not dest_addr_chk)
 				dest_addr, dest_addr_idx = h.get_last_addr(self.account, display=not dest_addr_chk)
 				h.print_addrs(accts_data, self.account)
 				h.print_addrs(accts_data, self.account)
-			else:                 # sweep to wallet
+			elif self.dest_acct is None: # sweep to wallet
 				h.close_wallet('source')
 				h.close_wallet('source')
 				h2 = self.rpc(self, self.dest)
 				h2 = self.rpc(self, self.dest)
 				h2.open_wallet('destination')
 				h2.open_wallet('destination')
@@ -1597,6 +1599,31 @@ class MoneroWalletOps:
 
 
 				h2.close_wallet('destination')
 				h2.close_wallet('destination')
 				h.open_wallet('source', refresh=False)
 				h.open_wallet('source', refresh=False)
+			else: # sweep to specific account of wallet
+
+				def get_dest_addr_params(h, accts_data, dest_acct, label):
+					self.check_account_exists(accts_data, dest_acct)
+					h.print_addrs(accts_data, dest_acct)
+					dest_addr_chk = create_new_addr_maybe(h, dest_acct, label)
+					dest_addr, dest_addr_idx = h.get_last_addr(dest_acct, display=not dest_addr_chk)
+					h.print_addrs(accts_data, dest_acct)
+					return dest_addr, dest_addr_idx, dest_addr_chk
+
+				dest_acct = self.dest_acct
+
+				if self.dest == self.source:
+					dest_addr, dest_addr_idx, dest_addr_chk = get_dest_addr_params(
+						h, accts_data, dest_acct,
+						f'Sweep from account #{self.account} [{make_timestr()}]')
+				else:
+					h.close_wallet('source')
+					h2 = self.rpc(self, self.dest)
+					h2.open_wallet('destination')
+					dest_addr, dest_addr_idx, dest_addr_chk = get_dest_addr_params(
+						h2, h2.get_accts()[0], dest_acct,
+						f'Sweep from {self.source.idx}:{self.account} [{make_timestr()}]')
+					h2.close_wallet('destination')
+					h.open_wallet('source', refresh=False)
 
 
 			assert dest_addr_chk in (None, dest_addr), 'dest_addr_chk'
 			assert dest_addr_chk in (None, dest_addr), 'dest_addr_chk'
 
 
@@ -1607,8 +1634,10 @@ class MoneroWalletOps:
 		def add_desc(self):
 		def add_desc(self):
 			if self.dest is None:
 			if self.dest is None:
 				return ' to new address'
 				return ' to new address'
-			else:
+			elif self.dest_acct is None:
 				return f' to new account in wallet {self.dest.idx}'
 				return f' to new account in wallet {self.dest.idx}'
+			else:
+				return f' to account #{self.dest_acct} of wallet {self.dest.idx}'
 
 
 		def check_account_exists(self, accts_data, idx):
 		def check_account_exists(self, accts_data, idx):
 			max_acct = len(accts_data['subaddress_accounts']) - 1
 			max_acct = len(accts_data['subaddress_accounts']) - 1

+ 21 - 11
test/cmdtest_py_d/ct_xmrwallet.py

@@ -100,9 +100,11 @@ class CmdTestXMRWallet(CmdTestBase):
 		('set_label_alice',           'setting an address label (Alice, subaddress)'),
 		('set_label_alice',           'setting an address label (Alice, subaddress)'),
 		('sync_wallets_selected',     'syncing selected wallets'),
 		('sync_wallets_selected',     'syncing selected wallets'),
 
 
-		('sweep_to_address_proxy',    'sweeping to new address (via TX relay + proxy)'),
+		('sweep_to_wallet',           'sweeping to new account in another wallet'),
-		('sweep_to_account',          'sweeping to new account'),
+		('sweep_to_account',          'sweeping to specific account in same wallet'),
-		('sweep_to_address_noproxy',  'sweeping to new address (via TX relay, no proxy)'),
+		('sweep_to_wallet_account',   'sweeping to specific account in another wallet'),
+		('sweep_to_wallet_account_proxy', 'sweeping to specific account in another wallet (via TX relay + proxy)'),
+		('sweep_to_same_account_noproxy', 'sweeping to same account (via TX relay, no proxy)'),
 		('transfer_to_miner_proxy',   'transferring funds to Miner (via TX relay + proxy)'),
 		('transfer_to_miner_proxy',   'transferring funds to Miner (via TX relay + proxy)'),
 		('transfer_to_miner_noproxy', 'transferring funds to Miner (via TX relay, no proxy)'),
 		('transfer_to_miner_noproxy', 'transferring funds to Miner (via TX relay, no proxy)'),
 
 
@@ -580,17 +582,25 @@ class CmdTestXMRWallet(CmdTestBase):
 
 
 		return t if do_ret else amt if return_amt else t.ok()
 		return t if do_ret else amt if return_amt else t.ok()
 
 
-	def sweep_to_address_proxy(self):
+	def sweep_to_wallet(self):
-		self.do_op('sweep', 'alice', '1:0', self.tx_relay_daemon_proxy_parm, add_opts=['--priority=3'])
+		self.do_op('sweep', 'alice', '1:0,2')
-		return self.mine_chk('alice',1,0,lambda x: x.ub > 1,'unlocked balance > 1')
+		return self.mine_chk('alice', 2, 1, lambda x: x.ub > 1, 'unlocked balance > 1')
 
 
 	def sweep_to_account(self):
 	def sweep_to_account(self):
-		self.do_op('sweep','alice','1:0,2')
+		self.do_op('sweep', 'alice', '2:1,2:0', use_existing=True)
-		return self.mine_chk('alice',2,1,lambda x: x.ub > 1,'unlocked balance > 1')
+		return self.mine_chk('alice', 2, 0, lambda x: x.ub > 1, 'unlocked balance > 1')
 
 
-	def sweep_to_address_noproxy(self):
+	def sweep_to_wallet_account(self):
-		self.do_op('sweep','alice','2:1',self.tx_relay_daemon_parm)
+		self.do_op('sweep', 'alice', '2:0,3:0', use_existing=True)
-		return self.mine_chk('alice',2,1,lambda x: x.ub > 0.9,'unlocked balance > 0.9')
+		return self.mine_chk('alice', 3, 0, lambda x: x.ub > 1, 'unlocked balance > 1')
+
+	def sweep_to_wallet_account_proxy(self):
+		self.do_op('sweep', 'alice', '3:0,2:1', self.tx_relay_daemon_proxy_parm, add_opts=['--priority=3'])
+		return self.mine_chk('alice', 2, 1, lambda x: x.ub > 1, 'unlocked balance > 1')
+
+	def sweep_to_same_account_noproxy(self):
+		self.do_op('sweep', 'alice', '2:1', self.tx_relay_daemon_parm)
+		return self.mine_chk('alice', 2, 1, lambda x: x.ub > 0.9, 'unlocked balance > 0.9')
 
 
 	async def transfer_to_miner_proxy(self):
 	async def transfer_to_miner_proxy(self):
 		addr = read_from_file(self.users['miner'].addrfile_fs.format(2))
 		addr = read_from_file(self.users['miner'].addrfile_fs.format(2))