mmgen-tool xmrwallet: add 'daemon', 'tx_relay_daemon' opts
- If 'daemon' is specified, RPC wallet connects to this monerod instead of the default at localhost:18081 - If 'tx_relay_daemon' is specified, transactions are relayed via this daemon instead of the local one. This option supports proxying via SOCKS (Tor)
This commit is contained in:
parent
af7be077e9
commit
0c42587978
2 changed files with 136 additions and 38 deletions
|
|
@ -230,6 +230,8 @@ class MoneroWalletDaemon(Daemon):
|
|||
host = None,
|
||||
user = None,
|
||||
passwd = None,
|
||||
daemon_addr = None,
|
||||
proxy = None,
|
||||
rpc_port_shift = None ):
|
||||
|
||||
super().__init__()
|
||||
|
|
@ -245,7 +247,10 @@ class MoneroWalletDaemon(Daemon):
|
|||
self.pidfile = os.path.join(self.datadir,id_str+'.pid')
|
||||
self.logfile = os.path.join(self.datadir,id_str+'.log')
|
||||
|
||||
self.daemon_port = CoinDaemon('xmr',test_suite=test_suite).rpc_port
|
||||
self.proxy = proxy
|
||||
self.daemon_addr = daemon_addr
|
||||
if not daemon_addr:
|
||||
self.daemon_port = CoinDaemon('xmr',test_suite=test_suite).rpc_port
|
||||
|
||||
if self.platform == 'win':
|
||||
self.use_pidfile = False
|
||||
|
|
@ -267,11 +272,20 @@ class MoneroWalletDaemon(Daemon):
|
|||
def start_cmd(self):
|
||||
cmd = [
|
||||
'monero-wallet-rpc',
|
||||
'--daemon-port={}'.format(self.daemon_port),
|
||||
'--untrusted-daemon',
|
||||
'--rpc-bind-port={}'.format(self.rpc_port),
|
||||
'--wallet-dir='+self.wallet_dir,
|
||||
'--log-file='+self.logfile,
|
||||
'--rpc-login={}:{}'.format(self.user,self.passwd) ]
|
||||
|
||||
if self.daemon_addr:
|
||||
cmd.append(f'--daemon-address={self.daemon_addr}')
|
||||
else:
|
||||
cmd.append(f'--daemon-port={self.daemon_port}')
|
||||
|
||||
if self.proxy:
|
||||
cmd.append(f'--proxy={self.proxy}')
|
||||
|
||||
if self.platform == 'linux':
|
||||
cmd += ['--pidfile={}'.format(self.pidfile)]
|
||||
cmd += [] if 'no_daemonize' in self.flags else ['--detach']
|
||||
|
|
|
|||
156
mmgen/tool.py
156
mmgen/tool.py
|
|
@ -1024,11 +1024,20 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
wallets: '(integer range or list, or sweep specifier)' = '',
|
||||
start_wallet_daemon = True,
|
||||
stop_wallet_daemon = True,
|
||||
daemon: 'HOST:PORT' = '',
|
||||
tx_relay_daemon: 'HOST:PORT[:PROXY_HOST:PROXY_PORT]' = '',
|
||||
):
|
||||
|
||||
"""
|
||||
perform various Monero wallet operations for addresses in XMR key-address file
|
||||
|
||||
Requires a running monerod daemon. Unless 'daemon' is specified, the daemon
|
||||
is assumed to be listening on localhost at the default RPC port.
|
||||
|
||||
If 'tx_relay_daemon' is specified, the monerod daemon at HOST:PORT will be
|
||||
used to relay any created transactions. PROXY_HOST:PROXY_PORT, if specified,
|
||||
may point to a Tor SOCKS proxy, in which case HOST may be a Tor onion address.
|
||||
|
||||
Supported operations:
|
||||
|
||||
create - create wallet for all or specified addresses in key-address file
|
||||
|
|
@ -1061,6 +1070,7 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
class base:
|
||||
|
||||
wallet_exists = True
|
||||
tx_relay = False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
|
|
@ -1087,7 +1097,8 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
from .daemon import MoneroWalletDaemon
|
||||
self.wd = MoneroWalletDaemon(
|
||||
wallet_dir = opt.outdir or '.',
|
||||
test_suite = g.test_suite
|
||||
test_suite = g.test_suite,
|
||||
daemon_addr = daemon or None,
|
||||
)
|
||||
|
||||
if start_wallet_daemon:
|
||||
|
|
@ -1115,6 +1126,8 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
def stop_daemons(self):
|
||||
if stop_wallet_daemon:
|
||||
self.wd.stop()
|
||||
if tx_relay_daemon:
|
||||
self.wd2.stop()
|
||||
|
||||
def post_init(self): pass
|
||||
def post_process(self): pass
|
||||
|
|
@ -1218,12 +1231,9 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
return True
|
||||
|
||||
def post_init(self):
|
||||
from .daemon import CoinDaemon
|
||||
md = CoinDaemon(network_id='xmr',test_suite=g.test_suite)
|
||||
host,port = (md.host,md.rpc_port)
|
||||
|
||||
host,port = daemon.split(':') if daemon else ('localhost',self.wd.daemon_port)
|
||||
from .rpc import MoneroRPCClient
|
||||
self.dc = MoneroRPCClient(host=host, port=port, user=None, passwd=None)
|
||||
self.dc = MoneroRPCClient(host=host, port=int(port), user=None, passwd=None)
|
||||
self.accts_data = {}
|
||||
|
||||
def post_process(self):
|
||||
|
|
@ -1252,6 +1262,7 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
name = 'sweep'
|
||||
desc = 'Sweep'
|
||||
past = 'swept'
|
||||
tx_relay = True
|
||||
|
||||
def create_addr_data(self):
|
||||
m = re.match('(\d+):(\d+)(?:,(\d+))?$',wallets,re.ASCII)
|
||||
|
|
@ -1281,6 +1292,31 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
self.addr_data = list(gen())
|
||||
self.account = int(m[2])
|
||||
|
||||
def post_init(self):
|
||||
|
||||
if tx_relay_daemon:
|
||||
m = re.fullmatch(hostproxy_pat,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 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
|
||||
|
|
@ -1306,31 +1342,48 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
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))
|
||||
h = xmr_rpc_methods(self,self.dest)
|
||||
await h.open_wallet('destination')
|
||||
accts_data = (await h.get_accts())[0]
|
||||
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 h.create_acct()
|
||||
await h.get_accts()
|
||||
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 = h.get_last_acct(accts_data)
|
||||
new_addr = h2.get_last_acct(accts_data)
|
||||
else:
|
||||
die(1,'Exiting at user request')
|
||||
|
||||
h = xmr_rpc_methods(self,self.source)
|
||||
await h2.close_wallet('destination')
|
||||
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)
|
||||
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 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:
|
||||
die(1,'Exiting at user request')
|
||||
await h.close_wallet('source')
|
||||
die(1,'\nExiting at user request')
|
||||
|
||||
return True
|
||||
|
||||
class xmr_rpc_methods:
|
||||
|
||||
|
|
@ -1348,6 +1401,11 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
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)}:')
|
||||
|
|
@ -1425,26 +1483,36 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
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'],
|
||||
}
|
||||
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
|
||||
)
|
||||
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]),
|
||||
))
|
||||
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)
|
||||
|
||||
def fmtXMRamt(amt):
|
||||
from .obj import XMRAmt
|
||||
return XMRAmt(amt,from_unit='min_coin_unit').fmt(fs='5.12',color=True)
|
||||
|
|
@ -1453,7 +1521,13 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
from .obj import XMRAmt
|
||||
return XMRAmt(amt,from_unit='min_coin_unit').hl()
|
||||
|
||||
def check_args():
|
||||
def check_args(localvars):
|
||||
|
||||
def check_host_arg(arg_name,pat):
|
||||
val = localvars[arg_name]
|
||||
if not re.fullmatch(pat,val,re.ASCII):
|
||||
annot = MMGenToolCmdMonero.xmrwallet.__annotations__[arg_name]
|
||||
die(1,f'{val!r}: invalid {arg_name!r} parameter: it must have format {annot!r}')
|
||||
|
||||
if blockheight < 0:
|
||||
die(1,f"{blockheight}: invalid 'blockheight' arg (<0)")
|
||||
|
|
@ -1464,8 +1538,18 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
if op == 'sync' and blockheight != 0:
|
||||
die(1,'Sync operation does not support blockheight arg')
|
||||
|
||||
if daemon:
|
||||
check_host_arg('daemon',host_pat)
|
||||
|
||||
if tx_relay_daemon:
|
||||
if not getattr(MoneroWalletOps,op).tx_relay:
|
||||
die(1,f"'tx_relay_daemon' arg is not recognized for operation {op!r}")
|
||||
check_host_arg('tx_relay_daemon',hostproxy_pat)
|
||||
|
||||
# start execution
|
||||
check_args()
|
||||
host_pat = r'(?:[^:]+):(?:\d+)'
|
||||
hostproxy_pat = r'({p})(?::({p}))?'.format(p=host_pat)
|
||||
check_args(locals())
|
||||
|
||||
m = getattr(MoneroWalletOps,op)()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue