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
This commit is contained in:
The MMGen Project 2024-04-08 16:14:26 +00:00
commit f39da52bc0
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 63 additions and 20 deletions

View file

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

View file

@ -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.

View file

@ -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

View file

@ -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))