From 8a750259b982f84fbe7fa55c1959814db4f452f0 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 22 Nov 2025 09:04:08 +0000 Subject: [PATCH] mmgen-xmrwallet: new `dump-json` operation --- mmgen/help/xmrwallet.py | 7 +++++-- mmgen/main_xmrwallet.py | 4 ++-- mmgen/xmrwallet/__init__.py | 4 ++++ mmgen/xmrwallet/ops/dump.py | 29 ++++++++++++++++++++++++++++- test/cmdtest_d/xmr_autosign.py | 20 ++++++++++++++------ 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/mmgen/help/xmrwallet.py b/mmgen/help/xmrwallet.py index 71ea1c83..63663b34 100755 --- a/mmgen/help/xmrwallet.py +++ b/mmgen/help/xmrwallet.py @@ -59,8 +59,11 @@ abort - abort the current transaction created with --autosign. The transaction may be signed or unsigned txview - display detailed information about a transaction file or files txlist - same as above, but display terse information in tabular format -dump - produce JSON dumps of wallet metadata (accounts, addresses and - labels) for a list or range of wallets +dump-json - dump wallet metadata (accounts, addresses, labels), plus address + balances, for a list or range of wallets, to standard output in + JSON format +dump - same as above, but dump metadata only and save the dumps to + separate files for each wallet restore - same as ‘create’, but additionally restore wallet metadata from the corresponding JSON dump files created with ‘dump’ export-outputs - export outputs of watch-only wallets for import into diff --git a/mmgen/main_xmrwallet.py b/mmgen/main_xmrwallet.py index 78390b0c..1315393f 100755 --- a/mmgen/main_xmrwallet.py +++ b/mmgen/main_xmrwallet.py @@ -28,7 +28,7 @@ opts_data = { 'desc': """Perform various Monero wallet and transacting operations for addresses in an MMGen XMR key-address file""", 'usage2': [ - '[opts] create | sync | list | view | listview | dump | restore [xmr_keyaddrfile] [wallets]', + '[opts] create | sync | list | view | listview | dump-json | dump | restore [xmr_keyaddrfile] [wallets]', '[opts] label [xmr_keyaddrfile] LABEL_SPEC', '[opts] new [xmr_keyaddrfile] NEW_ADDRESS_SPEC', '[opts] transfer [xmr_keyaddrfile] TRANSFER_SPEC', @@ -133,7 +133,7 @@ match op: cfg._usage() case 'txview' | 'txlist': infile = [infile] + cmd_args - case 'create' | 'sync' | 'list' | 'view' | 'listview' | 'dump' | 'restore': # kafile_arg_ops + case 'create' | 'sync' | 'list' | 'view' | 'listview' | 'dump_json' | 'dump' | 'restore': if len(cmd_args) > 1: cfg._usage() wallets = cmd_args.pop(0) if cmd_args else None diff --git a/mmgen/xmrwallet/__init__.py b/mmgen/xmrwallet/__init__.py index 691656f2..af95a4fe 100755 --- a/mmgen/xmrwallet/__init__.py +++ b/mmgen/xmrwallet/__init__.py @@ -64,6 +64,8 @@ op_names = { 'submit': 'submit', 'resubmit': 'submit', 'abort': 'submit', + 'dump_data': 'dump', + 'dump_json': 'dump', 'dump': 'dump', 'restore': 'restore', 'export_outputs': 'export', @@ -84,6 +86,8 @@ kafile_arg_ops = ( 'transfer', 'sweep', 'sweep_all', + 'dump_data', + 'dump_json', 'dump', 'restore') diff --git a/mmgen/xmrwallet/ops/dump.py b/mmgen/xmrwallet/ops/dump.py index c8d174bf..79a52940 100755 --- a/mmgen/xmrwallet/ops/dump.py +++ b/mmgen/xmrwallet/ops/dump.py @@ -12,7 +12,7 @@ xmrwallet.ops.dump: Monero wallet ops for the MMGen Suite """ -from ...util import msg +from ...util import msg, Msg from ..file.outputs import MoneroWalletDumpFile from ..rpc import MoneroWalletRPC @@ -33,3 +33,30 @@ class OpDump(OpWallet): data = {'wallet_metadata': wallet_data.addrs_data} ).write() return True + +class OpDumpDataBase(OpWallet): + wallet_offline = True + stem = 'dump' + return_data = True + + async def process_wallet(self, d, fn, last): + h = MoneroWalletRPC(self, d) + h.open_wallet('source') + return { + 'seed_id': self.kal.al_id.sid, + 'wallet_num': d.idx, + 'data': h.get_wallet_data(print=False)} + + def post_main_success(self): + pass + +class OpDumpData(OpDumpDataBase): + start_daemon = False + +class OpDumpJson(OpDumpDataBase): + + async def main(self): + import json + data = await super().main() + Msg(json.dumps(data)) + return sum(map(bool, data)) diff --git a/test/cmdtest_d/xmr_autosign.py b/test/cmdtest_d/xmr_autosign.py index cf368259..81edc504 100755 --- a/test/cmdtest_d/xmr_autosign.py +++ b/test/cmdtest_d/xmr_autosign.py @@ -17,8 +17,8 @@ import re, asyncio from mmgen.color import blue, cyan, brown -from ..include.common import imsg, silence, end_silence -from .include.common import get_file_with_ext +from ..include.common import imsg, silence, end_silence, strip_ansi_escapes +from .include.common import get_file_with_ext, cleanup_env from .xmrwallet import CmdTestXMRWallet from .autosign import CmdTestAutosignThreaded @@ -55,6 +55,7 @@ class CmdTestXMRAutosign(CmdTestXMRWallet, CmdTestAutosignThreaded): ('new_address_alice', 'adding an address to Alice’s tmp wallet'), ('new_address_alice_label', 'adding an address to Alice’s tmp wallet (with label)'), ('dump_tmp_wallets', 'dumping Alice’s tmp wallets'), + ('dump_tmp_wallets_json', 'dumping Alice’s tmp wallets to JSON format'), ('delete_tmp_wallets', 'deleting Alice’s tmp wallets'), ('gen_kafile_miner', 'generating key-address file for Miner'), ('create_wallet_miner', 'creating Monero wallet for Miner'), @@ -174,10 +175,13 @@ class CmdTestXMRAutosign(CmdTestXMRWallet, CmdTestAutosignThreaded): def dump_tmp_wallets(self): return self._dump_wallets(autosign=False) + def dump_tmp_wallets_json(self): + return self._dump_wallets(autosign=False, op='dump_json') + def dump_wallets(self): return self._dump_wallets(autosign=True) - def _dump_wallets(self, autosign): + def _dump_wallets(self, autosign, op='dump'): data = self.users['alice'] self.insert_device_online() t = self.spawn( @@ -186,10 +190,14 @@ class CmdTestXMRAutosign(CmdTestXMRWallet, CmdTestAutosignThreaded): + (['--alice', '--compat'] if self.compat else [f'--wallet-dir={data.udir}']) + [f'--daemon=localhost:{data.md.rpc_port}'] + (self.autosign_opts if autosign else []) - + ['dump'] - + ([] if autosign else [get_file_with_ext(data.udir, 'akeys')])) + + [op] + + ([] if autosign else [get_file_with_ext(data.udir, 'akeys')]), + env = cleanup_env(self.cfg)) t.expect('2 wallets dumped') - t.read() + res = t.read() + if op == 'dump_json': + import json + data = json.loads(re.sub('Stopping.*', '', strip_ansi_escapes(res)).strip()) self.remove_device_online() return t