From f39da52bc06f27ac20ab5bb643306403f5ebd220 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 8 Apr 2024 16:14:26 +0000 Subject: [PATCH] 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 --- mmgen/data/version | 2 +- mmgen/help/xmrwallet.py | 12 ++++++---- mmgen/xmrwallet.py | 37 +++++++++++++++++++++++++++---- test/cmdtest_py_d/ct_xmrwallet.py | 32 +++++++++++++++++--------- 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/mmgen/data/version b/mmgen/data/version index b2bfbe03..55d32778 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -14.1.dev26 +14.1.dev27 diff --git a/mmgen/help/xmrwallet.py b/mmgen/help/xmrwallet.py index 6a110b4d..0fbe77ae 100755 --- a/mmgen/help/xmrwallet.py +++ b/mmgen/help/xmrwallet.py @@ -108,14 +108,18 @@ The sweep operation takes a SWEEP_SPEC arg with the following format: {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 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 -newly created account in DEST, or the last existing account, if requested -by the user. +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. The user is prompted before addresses are created or funds transferred. diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index f0e0e8cc..5d453367 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -70,7 +70,7 @@ xmrwallet_uarg_info = ( 'tx_relay_daemon': e('HOST:PORT[:PROXY_IP:PROXY_PORT]', rf'({hp})(?::({hp}))?'), 'newaddr_spec': e('WALLET[:ACCOUNT][,"label text"]', r'(\d+)(?::(\d+))?(?:,(.*))?'), '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+),(.*)'), })( namedtuple('uarg_info_entry',['annot','pat']), @@ -1517,7 +1517,9 @@ class MoneroWalletOps: else: 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.amount = self.proto.coin_amt(m[4]) elif self.name == 'new': @@ -1577,7 +1579,7 @@ class MoneroWalletOps: 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) h.print_addrs(accts_data, self.account) - else: # sweep to wallet + elif self.dest_acct is None: # sweep to wallet h.close_wallet('source') h2 = self.rpc(self, self.dest) h2.open_wallet('destination') @@ -1597,6 +1599,31 @@ class MoneroWalletOps: h2.close_wallet('destination') 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' @@ -1607,8 +1634,10 @@ class MoneroWalletOps: def add_desc(self): if self.dest is None: return ' to new address' - else: + elif self.dest_acct is None: 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): max_acct = len(accts_data['subaddress_accounts']) - 1 diff --git a/test/cmdtest_py_d/ct_xmrwallet.py b/test/cmdtest_py_d/ct_xmrwallet.py index daac1f60..9c7abdbb 100755 --- a/test/cmdtest_py_d/ct_xmrwallet.py +++ b/test/cmdtest_py_d/ct_xmrwallet.py @@ -100,9 +100,11 @@ class CmdTestXMRWallet(CmdTestBase): ('set_label_alice', 'setting an address label (Alice, subaddress)'), ('sync_wallets_selected', 'syncing selected wallets'), - ('sweep_to_address_proxy', 'sweeping to new address (via TX relay + proxy)'), - ('sweep_to_account', 'sweeping to new account'), - ('sweep_to_address_noproxy', 'sweeping to new address (via TX relay, no proxy)'), + ('sweep_to_wallet', 'sweeping to new account in another wallet'), + ('sweep_to_account', 'sweeping to specific account in same wallet'), + ('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_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() - def sweep_to_address_proxy(self): - self.do_op('sweep', 'alice', '1:0', self.tx_relay_daemon_proxy_parm, add_opts=['--priority=3']) - return self.mine_chk('alice',1,0,lambda x: x.ub > 1,'unlocked balance > 1') + def sweep_to_wallet(self): + self.do_op('sweep', 'alice', '1:0,2') + return self.mine_chk('alice', 2, 1, lambda x: x.ub > 1, 'unlocked balance > 1') def sweep_to_account(self): - self.do_op('sweep','alice','1:0,2') - return self.mine_chk('alice',2,1,lambda x: x.ub > 1,'unlocked balance > 1') + self.do_op('sweep', 'alice', '2:1,2:0', use_existing=True) + return self.mine_chk('alice', 2, 0, lambda x: x.ub > 1, 'unlocked balance > 1') - def sweep_to_address_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') + def sweep_to_wallet_account(self): + self.do_op('sweep', 'alice', '2:0,3:0', use_existing=True) + 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): addr = read_from_file(self.users['miner'].addrfile_fs.format(2))