|
@@ -20,8 +20,6 @@
|
|
|
tool.py: Routines for the 'mmgen-tool' utility
|
|
|
"""
|
|
|
|
|
|
-import re
|
|
|
-from collections import namedtuple
|
|
|
from .protocol import hash160
|
|
|
from .common import *
|
|
|
from .crypto import *
|
|
@@ -1012,25 +1010,6 @@ class MMGenToolCmdRPC(MMGenToolCmds):
|
|
|
msg("Address '{}' deleted from tracking wallet".format(ret))
|
|
|
return ret
|
|
|
|
|
|
-from .obj import XMRAmt
|
|
|
-
|
|
|
-def fmtXMRamt(amt):
|
|
|
- return XMRAmt(amt,from_unit='min_coin_unit').fmt(fs='5.12',color=True)
|
|
|
-
|
|
|
-def hlXMRamt(amt):
|
|
|
- return XMRAmt(amt,from_unit='min_coin_unit').hl()
|
|
|
-
|
|
|
-def make_uarg_info():
|
|
|
- e = namedtuple('uarg_info_entry',['annot','pat'])
|
|
|
- hp = r'(?:[^:]+):(?:\d+)'
|
|
|
- return {
|
|
|
- 'daemon': e('HOST:PORT', hp),
|
|
|
- 'tx_relay_daemon': e('HOST:PORT[:PROXY_HOST:PROXY_PORT]', r'({p})(?::({p}))?'.format(p=hp)),
|
|
|
- 'wallets_sweep': e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
|
|
|
- }
|
|
|
-
|
|
|
-uarg_info = make_uarg_info()
|
|
|
-
|
|
|
class MMGenToolCmdMonero(MMGenToolCmds):
|
|
|
"""
|
|
|
Monero wallet operations
|
|
@@ -1040,6 +1019,8 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|
|
a violation of good security practice.
|
|
|
"""
|
|
|
|
|
|
+ from .xmrwallet import xmrwallet_uarg_info
|
|
|
+
|
|
|
def xmrwallet(
|
|
|
self,
|
|
|
op: str,
|
|
@@ -1048,8 +1029,8 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|
|
wallets: '(integer range or list, or sweep specifier)' = '',
|
|
|
start_wallet_daemon = True,
|
|
|
stop_wallet_daemon = True,
|
|
|
- daemon: uarg_info['daemon'].annot = '',
|
|
|
- tx_relay_daemon: uarg_info['tx_relay_daemon'].annot = '',
|
|
|
+ daemon: xmrwallet_uarg_info['daemon'].annot = '',
|
|
|
+ tx_relay_daemon: xmrwallet_uarg_info['tx_relay_daemon'].annot = '',
|
|
|
):
|
|
|
|
|
|
"""
|
|
@@ -1087,481 +1068,8 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|
|
The user is prompted before addresses are created or funds are transferred.
|
|
|
"""
|
|
|
|
|
|
- class MoneroWalletOps:
|
|
|
-
|
|
|
- ops = ('create','sync','sweep')
|
|
|
-
|
|
|
- class base:
|
|
|
+ from .xmrwallet import MoneroWalletOps
|
|
|
|
|
|
- wallet_exists = True
|
|
|
- tx_relay = False
|
|
|
-
|
|
|
- def check_uargs(self):
|
|
|
-
|
|
|
- def check_host_arg(name):
|
|
|
- val = getattr(uarg,name)
|
|
|
- if not re.fullmatch(uarg_info[name].pat,val,re.ASCII):
|
|
|
- die(1,'{!r}: invalid {!r} parameter: it must have format {!r}'.format(
|
|
|
- val, name, uarg_info[name].annot ))
|
|
|
-
|
|
|
- if uarg.op != 'create' and uarg.restore_height != 0:
|
|
|
- die(1,"'restore_height' arg is supported only for create operation")
|
|
|
-
|
|
|
- if uarg.restore_height < 0:
|
|
|
- die(1,f"{uarg.restore_height}: invalid 'restore_height' arg (<0)")
|
|
|
-
|
|
|
- if uarg.daemon:
|
|
|
- check_host_arg('daemon')
|
|
|
-
|
|
|
- if uarg.tx_relay_daemon:
|
|
|
- if not self.tx_relay:
|
|
|
- die(1,f"'tx_relay_daemon' arg is not recognized for operation {uarg.op!r}")
|
|
|
- check_host_arg('tx_relay_daemon')
|
|
|
-
|
|
|
- def __init__(self,uarg_tuple):
|
|
|
-
|
|
|
- def wallet_exists(fn):
|
|
|
- try: os.stat(fn)
|
|
|
- except: return False
|
|
|
- else: return True
|
|
|
-
|
|
|
- def check_wallets():
|
|
|
- for d in self.addr_data:
|
|
|
- fn = self.get_wallet_fn(d)
|
|
|
- exists = wallet_exists(fn)
|
|
|
- if exists and not self.wallet_exists:
|
|
|
- die(1,f'Wallet {fn!r} already exists!')
|
|
|
- elif not exists and self.wallet_exists:
|
|
|
- die(1,f'Wallet {fn!r} not found!')
|
|
|
-
|
|
|
- global uarg
|
|
|
- uarg = uarg_tuple
|
|
|
-
|
|
|
- self.check_uargs()
|
|
|
-
|
|
|
- from .protocol import init_proto
|
|
|
- self.kal = KeyAddrList(init_proto('xmr',network='mainnet'),uarg.xmr_keyaddrfile)
|
|
|
- self.create_addr_data()
|
|
|
-
|
|
|
- check_wallets()
|
|
|
-
|
|
|
- from .daemon import MoneroWalletDaemon
|
|
|
- self.wd = MoneroWalletDaemon(
|
|
|
- wallet_dir = opt.outdir or '.',
|
|
|
- test_suite = g.test_suite,
|
|
|
- daemon_addr = uarg.daemon or None,
|
|
|
- )
|
|
|
-
|
|
|
- if uarg.start_wallet_daemon:
|
|
|
- self.wd.restart()
|
|
|
-
|
|
|
- from .rpc import MoneroWalletRPCClient
|
|
|
- self.c = MoneroWalletRPCClient(
|
|
|
- host = self.wd.host,
|
|
|
- port = self.wd.rpc_port,
|
|
|
- user = self.wd.user,
|
|
|
- passwd = self.wd.passwd
|
|
|
- )
|
|
|
-
|
|
|
- self.post_init()
|
|
|
-
|
|
|
- def create_addr_data(self):
|
|
|
- if uarg.wallets:
|
|
|
- idxs = AddrIdxList(uarg.wallets)
|
|
|
- self.addr_data = [d for d in self.kal.data if d.idx in idxs]
|
|
|
- if len(self.addr_data) != len(idxs):
|
|
|
- die(1,f'List {uarg.wallets!r} contains addresses not present in supplied key-address file')
|
|
|
- else:
|
|
|
- self.addr_data = self.kal.data
|
|
|
-
|
|
|
- def stop_daemons(self):
|
|
|
- if uarg.stop_wallet_daemon:
|
|
|
- self.wd.stop()
|
|
|
- if uarg.tx_relay_daemon:
|
|
|
- self.wd2.stop()
|
|
|
-
|
|
|
- def post_init(self): pass
|
|
|
- def post_process(self): pass
|
|
|
-
|
|
|
- def get_wallet_fn(self,d):
|
|
|
- return os.path.join(
|
|
|
- opt.outdir or '.','{}-{}-MoneroWallet{}'.format(
|
|
|
- self.kal.al_id.sid,
|
|
|
- d.idx,
|
|
|
- '-α' if g.debug_utf8 else ''))
|
|
|
-
|
|
|
- async def process_wallets(self):
|
|
|
- gmsg('\n{}ing {} wallet{}'.format(self.desc,len(self.addr_data),suf(self.addr_data)))
|
|
|
- processed = 0
|
|
|
- for n,d in enumerate(self.addr_data): # [d.sec,d.addr,d.wallet_passwd,d.viewkey]
|
|
|
- fn = self.get_wallet_fn(d)
|
|
|
- gmsg('\n{}ing wallet {}/{} ({})'.format(
|
|
|
- self.desc,
|
|
|
- n+1,
|
|
|
- len(self.addr_data),
|
|
|
- os.path.basename(fn),
|
|
|
- ))
|
|
|
- processed += await self.run(d,fn)
|
|
|
- gmsg('\n{} wallet{} {}'.format(processed,suf(processed),self.past))
|
|
|
- return processed
|
|
|
-
|
|
|
- class create(base):
|
|
|
- name = 'create'
|
|
|
- desc = 'Creat'
|
|
|
- past = 'created'
|
|
|
- wallet_exists = False
|
|
|
-
|
|
|
- async def run(self,d,fn):
|
|
|
-
|
|
|
- from .baseconv import baseconv
|
|
|
- ret = await self.c.call(
|
|
|
- 'restore_deterministic_wallet',
|
|
|
- filename = os.path.basename(fn),
|
|
|
- password = d.wallet_passwd,
|
|
|
- seed = baseconv.fromhex(d.sec,'xmrseed',tostr=True),
|
|
|
- restore_height = uarg.restore_height,
|
|
|
- language = 'English' )
|
|
|
-
|
|
|
- pp_msg(ret) if opt.debug else msg(' Address: {}'.format(ret['address']))
|
|
|
- return True
|
|
|
-
|
|
|
- class sync(base):
|
|
|
- name = 'sync'
|
|
|
- desc = 'Sync'
|
|
|
- past = 'synced'
|
|
|
-
|
|
|
- async def run(self,d,fn):
|
|
|
-
|
|
|
- chain_height = (await self.dc.call('get_info'))['height']
|
|
|
- msg(f' Chain height: {chain_height}')
|
|
|
-
|
|
|
- import time
|
|
|
- t_start = time.time()
|
|
|
-
|
|
|
- msg_r(' Opening wallet...')
|
|
|
- await self.c.call(
|
|
|
- 'open_wallet',
|
|
|
- filename=os.path.basename(fn),
|
|
|
- password=d.wallet_passwd )
|
|
|
- msg('done')
|
|
|
-
|
|
|
- msg_r(' Getting wallet height (be patient, this could take a long time)...')
|
|
|
- wallet_height = (await self.c.call('get_height'))['height']
|
|
|
- msg_r('\r' + ' '*68 + '\r')
|
|
|
- msg(f' Wallet height: {wallet_height} ')
|
|
|
-
|
|
|
- behind = chain_height - wallet_height
|
|
|
- if behind > 1000:
|
|
|
- msg_r(f' Wallet is {behind} blocks behind chain tip. Please be patient. Syncing...')
|
|
|
-
|
|
|
- ret = await self.c.call('refresh')
|
|
|
-
|
|
|
- if behind > 1000:
|
|
|
- msg('done')
|
|
|
-
|
|
|
- if ret['received_money']:
|
|
|
- msg(' Wallet has received funds')
|
|
|
-
|
|
|
- t_elapsed = int(time.time() - t_start)
|
|
|
-
|
|
|
- bn = os.path.basename(fn)
|
|
|
-
|
|
|
- a,b = await xmr_rpc_methods(self,d).get_accts(print=False)
|
|
|
-
|
|
|
- msg(' Balance: {} Unlocked balance: {}'.format(
|
|
|
- hlXMRamt(a['total_balance']),
|
|
|
- hlXMRamt(a['total_unlocked_balance']),
|
|
|
- ))
|
|
|
-
|
|
|
- self.accts_data[bn] = { 'accts': a, 'addrs': b }
|
|
|
-
|
|
|
- msg(' Wallet height: {}'.format( (await self.c.call('get_height'))['height'] ))
|
|
|
- msg(' Sync time: {:02}:{:02}'.format( t_elapsed//60, t_elapsed%60 ))
|
|
|
-
|
|
|
- await self.c.call('close_wallet')
|
|
|
- return True
|
|
|
-
|
|
|
- def post_init(self):
|
|
|
- host,port = uarg.daemon.split(':') if uarg.daemon else ('localhost',self.wd.daemon_port)
|
|
|
- from .rpc import MoneroRPCClient
|
|
|
- self.dc = MoneroRPCClient(host=host, port=int(port), user=None, passwd=None)
|
|
|
- self.accts_data = {}
|
|
|
-
|
|
|
- def post_process(self):
|
|
|
- d = self.accts_data
|
|
|
-
|
|
|
- for n,k in enumerate(d):
|
|
|
- ad = self.addr_data[n]
|
|
|
- xmr_rpc_methods(self,ad).print_accts( d[k]['accts'], d[k]['addrs'], indent='')
|
|
|
-
|
|
|
- col1_w = max(map(len,d)) + 1
|
|
|
- fs = '{:%s} {} {}' % col1_w
|
|
|
- tbals = [0,0]
|
|
|
- msg('\n'+fs.format('Wallet','Balance ','Unlocked Balance'))
|
|
|
-
|
|
|
- for k in d:
|
|
|
- b = d[k]['accts']['total_balance']
|
|
|
- ub = d[k]['accts']['total_unlocked_balance']
|
|
|
- msg(fs.format( k + ':', fmtXMRamt(b), fmtXMRamt(ub) ))
|
|
|
- tbals[0] += b
|
|
|
- tbals[1] += ub
|
|
|
-
|
|
|
- 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'
|
|
|
- tx_relay = True
|
|
|
-
|
|
|
- def create_addr_data(self):
|
|
|
- m = re.fullmatch(uarg_info['wallets_sweep'].pat,uarg.wallets,re.ASCII)
|
|
|
- if not m:
|
|
|
- fs = "{!r}: invalid 'wallets' arg: for sweep operation, it must have format {!r}"
|
|
|
- die(1,fs.format( uarg.wallets, uarg_info['wallets_sweep'].annot ))
|
|
|
-
|
|
|
- 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])
|
|
|
-
|
|
|
- def post_init(self):
|
|
|
-
|
|
|
- if uarg.tx_relay_daemon:
|
|
|
- m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uarg.tx_relay_daemon,re.ASCII)
|
|
|
-
|
|
|
- from .daemon import MoneroWalletDaemon
|
|
|
- self.wd2 = MoneroWalletDaemon(
|
|
|
- wallet_dir = opt.outdir or '.',
|
|
|
- test_suite = g.test_suite,
|
|
|
- daemon_addr = m[1],
|
|
|
- proxy = m[2],
|
|
|
- rpc_port_shift = 16,
|
|
|
- )
|
|
|
-
|
|
|
- if uarg.start_wallet_daemon:
|
|
|
- self.wd2.restart()
|
|
|
-
|
|
|
- from .rpc import MoneroWalletRPCClient
|
|
|
- self.c2 = MoneroWalletRPCClient(
|
|
|
- host = self.wd2.host,
|
|
|
- port = self.wd2.rpc_port,
|
|
|
- user = self.wd2.user,
|
|
|
- passwd = self.wd2.passwd
|
|
|
- )
|
|
|
-
|
|
|
- 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())[0]
|
|
|
-
|
|
|
- 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:
|
|
|
- await h.close_wallet('source')
|
|
|
- bn = os.path.basename(self.get_wallet_fn(self.dest))
|
|
|
- h2 = xmr_rpc_methods(self,self.dest)
|
|
|
- await h2.open_wallet('destination')
|
|
|
- accts_data = (await h2.get_accts())[0]
|
|
|
-
|
|
|
- if keypress_confirm(f'\nCreate new account for wallet {bn!r}?'):
|
|
|
- new_addr = await h2.create_acct()
|
|
|
- await h2.get_accts()
|
|
|
- elif keypress_confirm(f'Sweep to last existing account of wallet {bn!r}?'):
|
|
|
- new_addr = h2.get_last_acct(accts_data)
|
|
|
- else:
|
|
|
- die(1,'Exiting at user request')
|
|
|
-
|
|
|
- await h2.close_wallet('destination')
|
|
|
- await h.open_wallet('source')
|
|
|
-
|
|
|
- msg('\nCreating sweep transaction: balance of wallet {}, account #{} => {}'.format(
|
|
|
- self.source.idx,
|
|
|
- self.account,
|
|
|
- cyan(new_addr),
|
|
|
- ))
|
|
|
- sweep_tx = await h.make_sweep_tx(self.account,new_addr)
|
|
|
-
|
|
|
- if keypress_confirm('Relay sweep transaction?'):
|
|
|
- w_desc = 'source'
|
|
|
- if uarg.tx_relay_daemon:
|
|
|
- await h.close_wallet('source')
|
|
|
- self.c = self.c2
|
|
|
- h = xmr_rpc_methods(self,self.source)
|
|
|
- w_desc = 'TX relay source'
|
|
|
- await h.open_wallet(w_desc)
|
|
|
- msg(f'\n Relaying sweep transaction...')
|
|
|
- await h.relay_sweep_tx( sweep_tx['tx_metadata_list'][0] )
|
|
|
- await h.close_wallet(w_desc)
|
|
|
-
|
|
|
- gmsg('\n\nAll done')
|
|
|
- else:
|
|
|
- await h.close_wallet('source')
|
|
|
- die(1,'\nExiting at user request')
|
|
|
-
|
|
|
- return True
|
|
|
-
|
|
|
- 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')
|
|
|
-
|
|
|
- async def close_wallet(self,desc):
|
|
|
- gmsg_r(f'\n Closing {desc} wallet...')
|
|
|
- await self.c.call('close_wallet')
|
|
|
- gmsg_r('done')
|
|
|
-
|
|
|
- def print_accts(self,data,addrs_data,indent=' '):
|
|
|
- d = data['subaddress_accounts']
|
|
|
- msg('\n' + indent + f'Accounts of wallet {os.path.basename(self.fn)}:')
|
|
|
- fs = indent + ' {:6} {:18} {:<6} {:%s} {}' % max(len(e['label']) for e in d)
|
|
|
- msg(fs.format('Index ','Base Address','nAddrs','Label','Balance'))
|
|
|
- for i,e in enumerate(d):
|
|
|
- msg(fs.format(
|
|
|
- str(e['account_index']),
|
|
|
- e['base_address'][:15] + '...',
|
|
|
- len(addrs_data[i]['addresses']),
|
|
|
- e['label'],
|
|
|
- fmtXMRamt(e['balance']),
|
|
|
- ))
|
|
|
-
|
|
|
- async def get_accts(self,print=True):
|
|
|
- data = await self.c.call('get_accounts')
|
|
|
- addrs_data = [
|
|
|
- await self.c.call('get_address',account_index=i)
|
|
|
- for i in range(len(data['subaddress_accounts']))
|
|
|
- ]
|
|
|
- if print:
|
|
|
- self.print_accts(data,addrs_data)
|
|
|
- return ( data, addrs_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
|
|
|
-
|
|
|
- def display_sweep_tx(self,data):
|
|
|
- from .obj import CoinTxID
|
|
|
- msg(' TxID: {}\n Amount: {}\n Fee: {}'.format(
|
|
|
- CoinTxID(data['tx_hash_list'][0]).hl(),
|
|
|
- hlXMRamt(data['amount_list'][0]),
|
|
|
- hlXMRamt(data['fee_list'][0]),
|
|
|
- ))
|
|
|
-
|
|
|
- async def make_sweep_tx(self,account,addr):
|
|
|
- ret = await self.c.call(
|
|
|
- 'sweep_all',
|
|
|
- address = addr,
|
|
|
- account_index = account,
|
|
|
- do_not_relay = True,
|
|
|
- get_tx_metadata = True
|
|
|
- )
|
|
|
- self.display_sweep_tx(ret)
|
|
|
- return ret
|
|
|
-
|
|
|
- def display_txid(self,data):
|
|
|
- from .obj import CoinTxID
|
|
|
- msg(' Relayed {}'.format( CoinTxID(data['tx_hash']).hl() ))
|
|
|
-
|
|
|
- async def relay_sweep_tx(self,tx_hex):
|
|
|
- ret = await self.c.call('relay_tx',hex=tx_hex)
|
|
|
- try:
|
|
|
- self.display_txid(ret)
|
|
|
- except:
|
|
|
- print(ret)
|
|
|
-
|
|
|
- # start execution
|
|
|
if op not in MoneroWalletOps.ops:
|
|
|
die(1,f'{op!r}: unrecognized operation')
|
|
|
|