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.
This commit is contained in:
The MMGen Project 2024-04-10 09:05:51 +00:00
commit fc7e3c8302
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 37 additions and 45 deletions

View file

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

View file

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

View file

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

View file

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

View file

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