mmgen-tool xmrwallet: new 'sweep' command
This commit is contained in:
parent
583b106817
commit
627d301076
1 changed files with 213 additions and 2 deletions
215
mmgen/tool.py
215
mmgen/tool.py
|
|
@ -1020,7 +1020,7 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
op: str,
|
||||
xmr_keyaddrfile: str,
|
||||
blockheight: '(default: current height)' = 0,
|
||||
wallets: '(integer range or list)' = '',
|
||||
wallets: '(integer range or list, or sweep specifier)' = '',
|
||||
start_wallet_daemon = True,
|
||||
stop_wallet_daemon = True,
|
||||
monerod_args = '',
|
||||
|
|
@ -1033,11 +1033,30 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
|
||||
create - create wallet for all or specified addresses in key-address file
|
||||
sync - sync wallet for all or specified addresses in key-address file
|
||||
sweep - sweep funds in specified wallet:account to new address in same
|
||||
account or new account in another wallet
|
||||
|
||||
SWEEP OPERATION NOTES
|
||||
|
||||
For the sweep operation, the parameter to the 'wallets' arg has a different
|
||||
format, known as a 'sweep specifier':
|
||||
|
||||
SOURCE:ACCOUNT[,DEST]
|
||||
|
||||
where SOURCE and DEST are wallet numbers and ACCOUNT an account index.
|
||||
|
||||
If DEST is omitted, a new address will be created in ACCOUNT of SOURCE and
|
||||
all funds from ACCOUNT of SOURCE will be swept into it.
|
||||
|
||||
If DEST is included, a new account will be created in DEST and all funds
|
||||
from ACCOUNT of SOURCE will be swept into the new account.
|
||||
|
||||
The user is prompted before addresses are created or funds are transferred.
|
||||
"""
|
||||
|
||||
class MoneroWalletOps:
|
||||
|
||||
ops = ('create','sync')
|
||||
ops = ('create','sync','sweep')
|
||||
|
||||
class base:
|
||||
|
||||
|
|
@ -1234,6 +1253,198 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
msg(fs.format( '-'*col1_w, '-'*18, '-'*18 ))
|
||||
msg(fs.format( 'TOTAL:', fmtXMRamt(tbals[0]), fmtXMRamt(tbals[1]) ))
|
||||
|
||||
class sweep(base):
|
||||
name = 'sweep'
|
||||
desc = 'Sweep'
|
||||
past = 'swept'
|
||||
|
||||
def create_addr_data(self):
|
||||
import re
|
||||
m = re.match('(\d+):(\d+)(?:,(\d+))?$',wallets,re.ASCII)
|
||||
if not m:
|
||||
die(1,
|
||||
"{!r}: invalid 'wallets' arg: for sweep operation, it must have format {}".format(
|
||||
wallets,
|
||||
'SOURCE:ACCOUNT[,DEST]'
|
||||
))
|
||||
|
||||
def gen():
|
||||
for i,k in ( (1,'source'), (3,'dest') ):
|
||||
if m[i] == None:
|
||||
setattr(self,k,None)
|
||||
else:
|
||||
idx = int(m[i])
|
||||
try:
|
||||
res = [d for d in self.kal.data if d.idx == idx][0]
|
||||
except:
|
||||
die(1,'Supplied key-address file does not contain address {}:{}'.format(
|
||||
self.kal.al_id.sid,
|
||||
idx ))
|
||||
else:
|
||||
setattr(self,k,res)
|
||||
yield res
|
||||
|
||||
self.addr_data = list(gen())
|
||||
self.account = int(m[2])
|
||||
|
||||
async def process_wallets(self):
|
||||
gmsg(f'\nSweeping account #{self.account} of wallet {self.source.idx}' + (
|
||||
' to new address' if self.dest is None else
|
||||
f' to new account in wallet {self.dest.idx}' ))
|
||||
|
||||
h = xmr_rpc_methods(self,self.source)
|
||||
|
||||
await h.open_wallet('source')
|
||||
accts_data = await h.get_accts()
|
||||
|
||||
max_acct = len(accts_data['subaddress_accounts']) - 1
|
||||
if self.account > max_acct:
|
||||
die(1,f'{self.account}: requested account index out of bounds (>{max_acct})')
|
||||
|
||||
await h.get_addrs(accts_data,self.account)
|
||||
|
||||
if self.dest == None:
|
||||
if keypress_confirm(f'\nCreate new address for account #{self.account}?'):
|
||||
new_addr = await h.create_new_addr(self.account)
|
||||
elif keypress_confirm(f'Sweep to last existing address of account #{self.account}?'):
|
||||
new_addr = await h.get_last_addr(self.account)
|
||||
else:
|
||||
die(1,'Exiting at user request')
|
||||
await h.get_addrs(accts_data,self.account)
|
||||
else:
|
||||
bn = os.path.basename(self.get_wallet_fn(self.dest))
|
||||
h = xmr_rpc_methods(self,self.dest)
|
||||
await h.open_wallet('destination')
|
||||
accts_data = await h.get_accts()
|
||||
|
||||
if keypress_confirm(f'\nCreate new account for wallet {bn!r}?'):
|
||||
new_addr = await h.create_acct()
|
||||
await h.get_accts()
|
||||
elif keypress_confirm(f'Sweep to last existing account of wallet {bn!r}?'):
|
||||
new_addr = h.get_last_acct(accts_data)
|
||||
else:
|
||||
die(1,'Exiting at user request')
|
||||
|
||||
h = xmr_rpc_methods(self,self.source)
|
||||
await h.open_wallet('source')
|
||||
|
||||
if keypress_confirm(
|
||||
'\nSweep balance of wallet {}, account #{} to {}?'.format(
|
||||
self.source.idx,
|
||||
self.account,
|
||||
cyan(new_addr),
|
||||
)):
|
||||
await h.do_sweep(self.account,new_addr)
|
||||
else:
|
||||
die(1,'Exiting at user request')
|
||||
|
||||
class xmr_rpc_methods:
|
||||
|
||||
def __init__(self,parent,d):
|
||||
self.parent = parent
|
||||
self.c = parent.c
|
||||
self.d = d
|
||||
self.fn = parent.get_wallet_fn(d)
|
||||
|
||||
async def open_wallet(self,desc):
|
||||
gmsg_r(f'\n Opening {desc} wallet...')
|
||||
ret = await self.c.call( # returns {}
|
||||
'open_wallet',
|
||||
filename=os.path.basename(self.fn),
|
||||
password=self.d.wallet_passwd )
|
||||
gmsg('done')
|
||||
|
||||
def print_accts(self,data,indent=' '):
|
||||
d = data['subaddress_accounts']
|
||||
msg('\n' + indent + f'Accounts of wallet {os.path.basename(self.fn)}:')
|
||||
fs = indent + ' {:6} {:18} {:%s} {}' % max(len(e['label']) for e in d)
|
||||
msg(fs.format('Index ','Base Address','Label','Balance'))
|
||||
for e in d:
|
||||
msg(fs.format(
|
||||
str(e['account_index']),
|
||||
e['base_address'][:15] + '...',
|
||||
e['label'],
|
||||
fmtXMRamt(e['balance']),
|
||||
))
|
||||
|
||||
async def get_accts(self):
|
||||
data = await self.c.call('get_accounts')
|
||||
self.print_accts(data)
|
||||
return data
|
||||
|
||||
async def create_acct(self):
|
||||
msg('\n Creating new account...')
|
||||
ret = await self.c.call(
|
||||
'create_account',
|
||||
label = f'Sweep from {self.parent.source.idx}:{self.parent.account}'
|
||||
)
|
||||
msg(' Index: {}'.format( pink(str(ret['account_index'])) ))
|
||||
msg(' Address: {}'.format( cyan(ret['address']) ))
|
||||
return ret['address']
|
||||
|
||||
def get_last_acct(self,accts_data):
|
||||
msg('\n Getting last account...')
|
||||
data = accts_data['subaddress_accounts'][-1]
|
||||
msg(' Index: {}'.format( pink(str(data['account_index'])) ))
|
||||
msg(' Address: {}'.format( cyan(data['base_address']) ))
|
||||
return data['base_address']
|
||||
|
||||
async def get_addrs(self,accts_data,account):
|
||||
ret = await self.c.call('get_address',account_index=account)
|
||||
d = ret['addresses']
|
||||
msg('\n Addresses of account #{} ({}):'.format(
|
||||
account,
|
||||
accts_data['subaddress_accounts'][account]['label']))
|
||||
fs = ' {:6} {:18} {:%s} {}' % max(len(e['label']) for e in d)
|
||||
msg(fs.format('Index ','Address','Label','Used'))
|
||||
for e in d:
|
||||
msg(fs.format(
|
||||
str(e['address_index']),
|
||||
e['address'][:15] + '...',
|
||||
e['label'],
|
||||
e['used']
|
||||
))
|
||||
return ret
|
||||
|
||||
async def create_new_addr(self,account):
|
||||
msg_r('\n Creating new address: ')
|
||||
ret = await self.c.call(
|
||||
'create_address',
|
||||
account_index = account,
|
||||
label = 'Sweep from this account',
|
||||
)
|
||||
msg(cyan(ret['address']))
|
||||
return ret['address']
|
||||
|
||||
async def get_last_addr(self,account):
|
||||
msg('\n Getting last address:')
|
||||
ret = (await self.c.call(
|
||||
'get_address',
|
||||
account_index = account,
|
||||
))['addresses'][-1]['address']
|
||||
msg(' ' + cyan(ret))
|
||||
return ret
|
||||
|
||||
async def do_sweep(self,account,addr):
|
||||
msg(f'\n Sweeping account balance...')
|
||||
ret = { # debug
|
||||
'amount_list': [322222330000],
|
||||
'fee_list': [10600000],
|
||||
'tx_hash_list': ['deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef'],
|
||||
}
|
||||
ret = await self.c.call(
|
||||
'sweep_all',
|
||||
address = addr,
|
||||
account_index = account,
|
||||
)
|
||||
from .obj import CoinTxID
|
||||
msg(' TxID: {}\n Amount: {}\n Fee: {}'.format(
|
||||
CoinTxID(ret['tx_hash_list'][0]).hl(),
|
||||
hlXMRamt(ret['amount_list'][0]),
|
||||
hlXMRamt(ret['fee_list'][0]),
|
||||
))
|
||||
return ret
|
||||
|
||||
def fmtXMRamt(amt):
|
||||
from .obj import XMRAmt
|
||||
return XMRAmt(amt,from_unit='min_coin_unit').fmt(fs='5.12',color=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue