new command: mmgen-xmrwallet, replacing mmgen-tool xmrwallet
- `wallets` arg is now 3rd positional arg
- tool keyword args are now command options
Old and new invocation examples:
OLD: mmgen-tool xmrwallet sync *.akeys.mmenc wallets=3-5
NEW: mmgen-xmrwallet sync *.akeys.mmenc 3-5
OLD: mmgen-tool xmrwallet sweep *.akeys.mmenc wallets=3:2 tx_relay_daemon=foo.onion:18081:127.0.0.1:9052
NEW: mmgen-xmrwallet --tx-relay-daemon=foo.onion:18081:127.0.0.1:9052 sweep *.akeys.mmenc 3:2
This commit is contained in:
parent
ecf489af0e
commit
cb98afda79
7 changed files with 210 additions and 100 deletions
24
cmds/mmgen-xmrwallet
Executable file
24
cmds/mmgen-xmrwallet
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2021 The MMGen Project <mmgen@tuta.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-xmrwallet: Monero wallet ops for the MMGen suite
|
||||
"""
|
||||
|
||||
from mmgen.main import launch
|
||||
launch("xmrwallet")
|
||||
166
mmgen/main_xmrwallet.py
Executable file
166
mmgen/main_xmrwallet.py
Executable file
|
|
@ -0,0 +1,166 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2021 The MMGen Project <mmgen@tuta.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen/main_xmrwallet: Perform various Monero wallet operations for addresses
|
||||
in an MMGen XMR key-address file
|
||||
"""
|
||||
|
||||
from .common import *
|
||||
from .xmrwallet import xmrwallet_uarg_info,MoneroWalletOps
|
||||
|
||||
opts_data = {
|
||||
'text': {
|
||||
'desc': """Perform various Monero wallet operations for addresses
|
||||
in an MMGen XMR key-address file""",
|
||||
'usage2': [
|
||||
'[opts] create <xmr_keyaddrfile> [wallets]',
|
||||
'[opts] sync <xmr_keyaddrfile> [wallets]',
|
||||
'[opts] sweep <xmr_keyaddrfile> <sweep_spec>',
|
||||
],
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
--, --longhelp Print help message for long options (common
|
||||
options)
|
||||
-d, --outdir=D Output or operate on wallets in directory 'D'
|
||||
instead of working dir
|
||||
-D, --daemon=H:P Connect to monerod at {D}
|
||||
-R, --tx-relay-daemon=H:P[:H:P] Relay transactions via monerod specified by
|
||||
{R}
|
||||
-k, --use-internal-keccak-module Force use of the internal keccak module
|
||||
-p, --hash-preset=P Use scrypt hash preset 'P' for password
|
||||
hashing (default: '{g.dfl_hash_preset}')
|
||||
-r, --restore-height=H Scan from height 'H' when creating wallets
|
||||
-s, --no-start-wallet-daemon Don’t start the wallet daemon at startup
|
||||
-S, --no-stop-wallet-daemon Don’t stop the wallet daemon at exit
|
||||
""",
|
||||
'notes': """
|
||||
|
||||
Command requires a running monerod daemon. Unless --daemon is specified,
|
||||
monerod 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 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
|
||||
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
|
||||
|
||||
|
||||
CREATE AND SYNC OPERATION NOTES
|
||||
|
||||
These operations take an optional `wallets` argument: a comma-separated list,
|
||||
hyphenated range, or combination of both, of address indexes in the specified
|
||||
key-address file, each corresponding to a Monero wallet to be created or
|
||||
synced. If omitted, all wallets are operated upon.
|
||||
|
||||
|
||||
SWEEP OPERATION NOTES
|
||||
|
||||
The sweep operation takes a `sweep specifier` arg with the following format:
|
||||
|
||||
SOURCE:ACCOUNT[,DEST]
|
||||
|
||||
where SOURCE and DEST are wallet indexes 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.
|
||||
|
||||
|
||||
WARNING
|
||||
|
||||
Note that the use of this command requires private data to be exposed on a
|
||||
network-connected machine in order to unlock the Monero wallets. This is a
|
||||
violation of good security practice.
|
||||
"""
|
||||
},
|
||||
'code': {
|
||||
'options': lambda s: s.format(
|
||||
D=xmrwallet_uarg_info['daemon'].annot,
|
||||
R=xmrwallet_uarg_info['tx_relay_daemon'].annot,
|
||||
g=g,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
cmd_args = opts.init(opts_data)
|
||||
|
||||
if len(cmd_args) < 2:
|
||||
opts.usage()
|
||||
|
||||
op = cmd_args.pop(0)
|
||||
infile = cmd_args.pop(0)
|
||||
|
||||
if op not in MoneroWalletOps.ops:
|
||||
die(1,f'{op!r}: unrecognized operation')
|
||||
|
||||
wallets = spec = ''
|
||||
|
||||
if op in ('create','sync'):
|
||||
if len(cmd_args) not in (0,1):
|
||||
opts.usage()
|
||||
if cmd_args:
|
||||
wallets = cmd_args[0]
|
||||
elif op == 'sweep':
|
||||
if len(cmd_args) != 1:
|
||||
opts.usage()
|
||||
spec = cmd_args[0]
|
||||
|
||||
ua = namedtuple('uargs',[
|
||||
'op',
|
||||
'xmr_keyaddrfile',
|
||||
'wallets',
|
||||
'spec',
|
||||
'daemon',
|
||||
'tx_relay_daemon',
|
||||
'restore_height',
|
||||
'start_wallet_daemon',
|
||||
'stop_wallet_daemon',
|
||||
])
|
||||
|
||||
uargs = ua(
|
||||
op,
|
||||
infile,
|
||||
wallets,
|
||||
spec,
|
||||
opt.daemon or '',
|
||||
opt.tx_relay_daemon or '',
|
||||
opt.restore_height or 0,
|
||||
not opt.no_start_wallet_daemon,
|
||||
not opt.no_stop_wallet_daemon,
|
||||
)
|
||||
|
||||
m = getattr(MoneroWalletOps,op)(uargs)
|
||||
|
||||
try:
|
||||
if run_session(m.process_wallets()):
|
||||
m.post_process()
|
||||
except KeyboardInterrupt:
|
||||
ymsg('\nUser interrupt')
|
||||
finally:
|
||||
m.stop_daemons()
|
||||
|
|
@ -1010,83 +1010,6 @@ class MMGenToolCmdRPC(MMGenToolCmds):
|
|||
msg("Address '{}' deleted from tracking wallet".format(ret))
|
||||
return ret
|
||||
|
||||
class MMGenToolCmdMonero(MMGenToolCmds):
|
||||
"""
|
||||
Monero wallet operations
|
||||
|
||||
Note that the use of these commands requires private data to be exposed on
|
||||
a network-connected machine in order to unlock the Monero wallets. This is
|
||||
a violation of good security practice.
|
||||
"""
|
||||
|
||||
from .xmrwallet import xmrwallet_uarg_info
|
||||
|
||||
def xmrwallet(
|
||||
self,
|
||||
op: str,
|
||||
xmr_keyaddrfile: str,
|
||||
restore_height = 0,
|
||||
wallets: '(integer range or list, or sweep specifier)' = '',
|
||||
start_wallet_daemon = True,
|
||||
stop_wallet_daemon = True,
|
||||
daemon: xmrwallet_uarg_info['daemon'].annot = '',
|
||||
tx_relay_daemon: xmrwallet_uarg_info['tx_relay_daemon'].annot = '',
|
||||
):
|
||||
|
||||
"""
|
||||
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
|
||||
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.
|
||||
"""
|
||||
|
||||
from .xmrwallet import MoneroWalletOps
|
||||
|
||||
if op not in MoneroWalletOps.ops:
|
||||
die(1,f'{op!r}: unrecognized operation')
|
||||
|
||||
m = getattr(MoneroWalletOps,op)(
|
||||
_create_argtuple( MMGenToolCmdMonero.xmrwallet, locals() )
|
||||
)
|
||||
|
||||
try:
|
||||
if run_session(m.process_wallets()):
|
||||
m.post_process()
|
||||
except KeyboardInterrupt:
|
||||
ymsg('\nUser interrupt')
|
||||
finally:
|
||||
m.stop_daemons()
|
||||
|
||||
return True
|
||||
|
||||
class tool_api(
|
||||
MMGenToolCmdUtil,
|
||||
MMGenToolCmdCoin,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ xmrwallet_uarg_info = (
|
|||
lambda e,hp: {
|
||||
'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+))?'),
|
||||
'sweep_spec': e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
|
||||
})(
|
||||
namedtuple('uarg_info_entry',['annot','pat']),
|
||||
r'(?:[^:]+):(?:\d+)'
|
||||
|
|
@ -408,10 +408,10 @@ class MoneroWalletOps:
|
|||
tx_relay = True
|
||||
|
||||
def create_addr_data(self):
|
||||
m = re.fullmatch(uarg_info['wallets_sweep'].pat,uarg.wallets,re.ASCII)
|
||||
m = re.fullmatch(uarg_info['sweep_spec'].pat,uarg.spec,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 ))
|
||||
fs = "{!r}: invalid 'sweep_spec' arg: for sweep operation, it must have format {!r}"
|
||||
die(1,fs.format( uarg.spec, uarg_info['sweep_spec'].annot ))
|
||||
|
||||
def gen():
|
||||
for i,k in ( (1,'source'), (3,'dest') ):
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -148,6 +148,7 @@ setup(
|
|||
'mmgen.txsign',
|
||||
'mmgen.util',
|
||||
'mmgen.wallet',
|
||||
'mmgen.xmrwallet',
|
||||
|
||||
'mmgen.altcoins.__init__',
|
||||
|
||||
|
|
@ -189,6 +190,7 @@ setup(
|
|||
'mmgen.main_txsend',
|
||||
'mmgen.main_txsign',
|
||||
'mmgen.main_wallet',
|
||||
'mmgen.main_xmrwallet',
|
||||
|
||||
'mmgen.share.__init__',
|
||||
'mmgen.share.Opts',
|
||||
|
|
@ -214,5 +216,6 @@ setup(
|
|||
'cmds/mmgen-walletchk',
|
||||
'cmds/mmgen-walletconv',
|
||||
'cmds/mmgen-walletgen',
|
||||
'cmds/mmgen-xmrwallet',
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
|
|||
from mmgen.protocol import init_proto
|
||||
self.proto = init_proto('XMR',network='testnet')
|
||||
self.datadir_base = os.path.join('test','daemons','xmrtest')
|
||||
self.tool_args = ['--testnet=1', '--monero-wallet-rpc-password=passw0rd']
|
||||
self.long_opts = ['--testnet=1', '--monero-wallet-rpc-password=passw0rd']
|
||||
self.init_users()
|
||||
self.init_daemon_args()
|
||||
|
||||
|
|
@ -283,11 +283,10 @@ class TestSuiteXMRWallet(TestSuiteBase):
|
|||
if not data.kal_range:
|
||||
continue
|
||||
run('rm -f {}*'.format( data.walletfile_fs.format('*') ),shell=True)
|
||||
dir_arg = [f'--outdir='+data.udir]
|
||||
cmd_opts = ['wallets={}'.format(data.kal_range)]
|
||||
dir_opt = [f'--outdir={data.udir}']
|
||||
t = self.spawn(
|
||||
'mmgen-tool',
|
||||
self.tool_args + dir_arg + [ 'xmrwallet', 'create', data.kafile ] + cmd_opts,
|
||||
'mmgen-xmrwallet',
|
||||
self.long_opts + dir_opt + [ 'create', data.kafile, data.kal_range ],
|
||||
extra_desc = f'({capfirst(user)})' )
|
||||
t.expect('Check key-to-address validity? (y/N): ','n')
|
||||
for i in MMGenRange(data.kal_range).items:
|
||||
|
|
@ -318,14 +317,11 @@ class TestSuiteXMRWallet(TestSuiteBase):
|
|||
|
||||
def sync_wallets(self,wallets=None):
|
||||
data = self.users['alice']
|
||||
dir_arg = [f'--outdir={data.udir}']
|
||||
cmd_opts = list_gen(
|
||||
[f'daemon=localhost:{data.md.rpc_port}'],
|
||||
[f'wallets={wallets}', wallets],
|
||||
)
|
||||
dir_opt = [f'--outdir={data.udir}']
|
||||
cmd_opts = [f'--daemon=localhost:{data.md.rpc_port}']
|
||||
t = self.spawn(
|
||||
'mmgen-tool',
|
||||
self.tool_args + dir_arg + [ 'xmrwallet', 'sync', data.kafile ] + cmd_opts )
|
||||
'mmgen-xmrwallet',
|
||||
self.long_opts + dir_opt + cmd_opts + [ 'sync', data.kafile ] + ([wallets] if wallets else []) )
|
||||
t.expect('Check key-to-address validity? (y/N): ','n')
|
||||
wlist = AddrIdxList(wallets) if wallets else MMGenRange(data.kal_range).items
|
||||
for n,wnum in enumerate(wlist):
|
||||
|
|
@ -342,15 +338,14 @@ class TestSuiteXMRWallet(TestSuiteBase):
|
|||
|
||||
def _sweep_user(self,user,spec,tx_relay_daemon=None):
|
||||
data = self.users[user]
|
||||
dir_arg = [f'--outdir='+data.udir]
|
||||
dir_opt = [f'--outdir={data.udir}']
|
||||
cmd_opts = list_gen(
|
||||
[f'daemon=localhost:{data.md.rpc_port}'],
|
||||
[f'wallets={spec}'],
|
||||
[f'tx_relay_daemon={tx_relay_daemon}', tx_relay_daemon]
|
||||
[f'--daemon=localhost:{data.md.rpc_port}'],
|
||||
[f'--tx-relay-daemon={tx_relay_daemon}', tx_relay_daemon]
|
||||
)
|
||||
t = self.spawn(
|
||||
'mmgen-tool',
|
||||
self.tool_args + dir_arg + [ 'xmrwallet', 'sweep', data.kafile ] + cmd_opts,
|
||||
'mmgen-xmrwallet',
|
||||
self.long_opts + dir_opt + cmd_opts + [ 'sweep', data.kafile, spec ],
|
||||
extra_desc = f'({capfirst(user)})' )
|
||||
t.expect('Check key-to-address validity? (y/N): ','n')
|
||||
t.expect(
|
||||
|
|
|
|||
|
|
@ -159,7 +159,6 @@ if opt.list_names:
|
|||
'addrfile_chksum','keyaddrfile_chksum','passwdfile_chksum',
|
||||
'add_label','remove_label','remove_address','twview',
|
||||
'getbalance','listaddresses','listaddress'),
|
||||
'test-release.sh': ('xmrwallet',),
|
||||
'tooltest2.py': run(tcmd,stdout=PIPE,check=True).stdout.decode().split()
|
||||
}
|
||||
for v in cmd_data.values():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue