Browse Source

`mmgen-tool tw{export,import}`: support editing of comments fields

The MMGen Project 2 years ago
parent
commit
30b94db2f8

+ 4 - 2
mmgen/base_proto/ethereum/tw/json.py

@@ -87,13 +87,15 @@ class EthereumTwJSON(TwJSON):
 
 
 		async def do_import(self,batch):
 		async def do_import(self,batch):
 
 
+			from ....obj import TwComment
+
 			def gen_data(data):
 			def gen_data(data):
 				for d in data:
 				for d in data:
 					if hasattr(d,'address'):
 					if hasattr(d,'address'):
 						if d.amount is None: # Python 3.9: {} | {}
 						if d.amount is None: # Python 3.9: {} | {}
-							yield (d.address, {'mmid':d.mmgen_id,'comment':d.comment})
+							yield (d.address, {'mmid':d.mmgen_id,'comment':TwComment(d.comment)})
 						else:
 						else:
-							yield (d.address, {'mmid':d.mmgen_id,'comment':d.comment,'balance':d.amount})
+							yield (d.address, {'mmid':d.mmgen_id,'comment':TwComment(d.comment),'balance':d.amount})
 					else:
 					else:
 						yield ('params', {'symbol':d.symbol,'decimals':d.decimals})
 						yield ('params', {'symbol':d.symbol,'decimals':d.decimals})
 
 

+ 1 - 1
mmgen/data/release_date

@@ -1 +1 @@
-June 2022
+July 2022

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.2.dev8
+13.2.dev9

+ 10 - 4
mmgen/tool/rpc.py

@@ -200,25 +200,31 @@ class tool_cmd(tool_cmd_base):
 		ret = await (await TrackingWallet(self.proto,mode='w')).rescan_blockchain(start_block,stop_block)
 		ret = await (await TrackingWallet(self.proto,mode='w')).rescan_blockchain(start_block,stop_block)
 		return True
 		return True
 
 
-	async def twexport(self,include_amts=True):
+	async def twexport(self,include_amts=True,pretty=False):
 		"""
 		"""
 		export a tracking wallet to JSON format
 		export a tracking wallet to JSON format
 
 
-		NOTE:
+		NOTES:
 
 
 		  If ‘include_amts’ is true (the default), Ethereum balances will be restored
 		  If ‘include_amts’ is true (the default), Ethereum balances will be restored
 		  from the dump upon import. For Bitcoin and forks, amount fields in the dump
 		  from the dump upon import. For Bitcoin and forks, amount fields in the dump
 		  are ignored.
 		  are ignored.
+
+		  If ‘pretty’ is true, JSON will be dumped in human-readable format to allow
+		  for editing of comment fields.
 		"""
 		"""
 		from ..tw.json import TwJSON
 		from ..tw.json import TwJSON
-		await TwJSON.Export( self.proto, include_amts=include_amts )
+		await TwJSON.Export( self.proto, include_amts=include_amts, pretty=pretty )
 		return True
 		return True
 
 
 	async def twimport(self,filename:str,ignore_checksum=False,batch=False):
 	async def twimport(self,filename:str,ignore_checksum=False,batch=False):
 		"""
 		"""
 		restore a tracking wallet from a JSON dump created by ‘twexport’
 		restore a tracking wallet from a JSON dump created by ‘twexport’
 
 
-		NOTE:
+		NOTES:
+
+		  If comment fields in the JSON dump have been edited, ‘ignore_checksum’ must
+		  be set to true.
 
 
 		  The restored tracking wallet will have correct balances but no record of
 		  The restored tracking wallet will have correct balances but no record of
 		  historical transactions.  These may be restored by running ‘mmgen-tool
 		  historical transactions.  These may be restored by running ‘mmgen-tool

+ 14 - 6
mmgen/tw/json.py

@@ -50,8 +50,13 @@ class TwJSON:
 		def dump_fn(self):
 		def dump_fn(self):
 			return f'{self.fn_pfx}-{self.coin}-{self.network}.json'
 			return f'{self.fn_pfx}-{self.coin}-{self.network}.json'
 
 
-		def json_dump(self,data):
-			return json.dumps( data, cls=json_encoder, separators=(',', ':'), sort_keys=True )
+		def json_dump(self,data,pretty=False):
+			return json.dumps(
+				data,
+				cls        = json_encoder,
+				sort_keys  = True,
+				separators = None if pretty else (',', ':'),
+				indent     = 4 if pretty else None )
 
 
 		def make_chksum(self,data):
 		def make_chksum(self,data):
 			return make_chksum_8( self.json_dump(data).encode() ).lower()
 			return make_chksum_8( self.json_dump(data).encode() ).lower()
@@ -128,7 +133,7 @@ class TwJSON:
 
 
 	class Export(Base,metaclass=AsyncInit):
 	class Export(Base,metaclass=AsyncInit):
 
 
-		async def __init__(self,proto,include_amts=True):
+		async def __init__(self,proto,include_amts=True,pretty=False):
 
 
 			super().__init__(proto)
 			super().__init__(proto)
 
 
@@ -156,7 +161,10 @@ class TwJSON:
 			from ..fileutil import write_data_to_file
 			from ..fileutil import write_data_to_file
 			write_data_to_file(
 			write_data_to_file(
 				outfile = self.dump_fn,
 				outfile = self.dump_fn,
-				data = self.json_dump({
-					'checksum': self.make_chksum(data),
-					'data': data }),
+				data = self.json_dump(
+					{
+						'checksum': self.make_chksum(data),
+						'data': data
+					},
+					pretty = pretty ),
 				desc = f'tracking wallet JSON data' )
 				desc = f'tracking wallet JSON data' )

+ 27 - 2
test/test_py_d/ts_ethdev.py

@@ -317,6 +317,12 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		('twmove',             'moving the tracking wallet'),
 		('twmove',             'moving the tracking wallet'),
 		('twimport',           'importing the tracking wallet'),
 		('twimport',           'importing the tracking wallet'),
 		('twcompare',          'comparing imported tracking wallet with original'),
 		('twcompare',          'comparing imported tracking wallet with original'),
+		('edit_json_twdump',   'editing the tracking wallet JSON dump'),
+		('twmove',             'moving the tracking wallet'),
+		('twimport_nochksum',  'importing the edited tracking wallet JSON dump (ignore_checksum=1)'),
+
+		('token_listaddresses3','listaddresses --token=mm1 showempty=1'),
+		('token_listaddresses4','listaddresses --token=mm2 showempty=1'),
 		('twview9',            'twview (check balance)'),
 		('twview9',            'twview (check balance)'),
 
 
 		('stop',               'stopping daemon'),
 		('stop',               'stopping daemon'),
@@ -1143,6 +1149,10 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		return self.listaddresses(args=['--token=mm1'])
 		return self.listaddresses(args=['--token=mm1'])
 	def token_listaddresses2(self):
 	def token_listaddresses2(self):
 		return self.listaddresses(args=['--token=mm1'],tool_args=['showempty=1'])
 		return self.listaddresses(args=['--token=mm1'],tool_args=['showempty=1'])
+	def token_listaddresses3(self):
+		return self.listaddresses(args=['--token=mm1'],tool_args=['showempty=1'])
+	def token_listaddresses4(self):
+		return self.listaddresses(args=['--token=mm2'],tool_args=['showempty=1'])
 
 
 	def twview_cached_balances(self):
 	def twview_cached_balances(self):
 		return self.twview(args=['--cached-balances'])
 		return self.twview(args=['--cached-balances'])
@@ -1279,14 +1289,19 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		os.rename( tw.tw_fn, tw.tw_fn+'.bak.json' )
 		os.rename( tw.tw_fn, tw.tw_fn+'.bak.json' )
 		return 'ok'
 		return 'ok'
 
 
-	def twimport(self,add_args=[]):
+	def twimport(self,add_args=[],expect_str=None):
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
 		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
 		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
-		t = self.spawn('mmgen-tool',self.eth_args + ['twimport',fn] + add_args)
+		t = self.spawn('mmgen-tool',self.eth_args_noquiet + ['twimport',fn] + add_args)
 		t.expect('(y/N): ','y')
 		t.expect('(y/N): ','y')
+		if expect_str:
+			t.expect(expect_str)
 		t.written_to_file('tracking wallet data')
 		t.written_to_file('tracking wallet data')
 		return t
 		return t
 
 
+	def twimport_nochksum(self):
+		return self.twimport(add_args=['ignore_checksum=true'],expect_str='ignoring incorrect checksum')
+
 	def tw_chktotal(self):
 	def tw_chktotal(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
@@ -1305,6 +1320,16 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		cmp_or_die(*data,'tracking wallets')
 		cmp_or_die(*data,'tracking wallets')
 		return 'ok'
 		return 'ok'
 
 
+	def edit_json_twdump(self):
+		self.spawn('',msg_only=True)
+		from mmgen.tw.json import TwJSON
+		fn = TwJSON.Base(self.proto).dump_fn
+		text = json.loads(self.read_from_tmpfile(fn))
+		token_addr = self.read_from_tmpfile('token_addr2').strip()
+		text['data']['entries']['tokens'][token_addr][2][3] = f'edited comment [фубар] [{gr_uc}]'
+		self.write_to_tmpfile( fn, json.dumps(text,indent=4) )
+		return 'ok'
+
 	def stop(self):
 	def stop(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		if not opt.no_daemon_stop:
 		if not opt.no_daemon_stop:

+ 29 - 2
test/test_py_d/ts_regtest.py

@@ -209,6 +209,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		('bob_rescan_blockchain_gb', 'rescanning the blockchain (Genesis block)'),
 		('bob_rescan_blockchain_gb', 'rescanning the blockchain (Genesis block)'),
 		('bob_rescan_blockchain_one','rescanning the blockchain (single block)'),
 		('bob_rescan_blockchain_one','rescanning the blockchain (single block)'),
 		('bob_rescan_blockchain_ss', 'rescanning the blockchain (range of blocks)'),
 		('bob_rescan_blockchain_ss', 'rescanning the blockchain (range of blocks)'),
+
 		('bob_twexport',             'exporting a tracking wallet to JSON'),
 		('bob_twexport',             'exporting a tracking wallet to JSON'),
 		('carol_twimport',           'importing a tracking wallet JSON dump'),
 		('carol_twimport',           'importing a tracking wallet JSON dump'),
 		('carol_delete_wallet',      'unloading and deleting Carol’s tracking wallet'),
 		('carol_delete_wallet',      'unloading and deleting Carol’s tracking wallet'),
@@ -216,6 +217,12 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		('carol_twimport_nochksum',  'importing a tracking wallet JSON dump (ignore_checksum=1)'),
 		('carol_twimport_nochksum',  'importing a tracking wallet JSON dump (ignore_checksum=1)'),
 		('carol_delete_wallet',      'unloading and deleting Carol’s tracking wallet'),
 		('carol_delete_wallet',      'unloading and deleting Carol’s tracking wallet'),
 		('carol_twimport_batch',     'importing a tracking wallet JSON dump (batch=1)'),
 		('carol_twimport_batch',     'importing a tracking wallet JSON dump (batch=1)'),
+		('bob_twexport_pretty',      'exporting a tracking wallet to JSON (pretty=1)'),
+		('bob_edit_json_twdump',     'editing a tracking wallet JSON dump'),
+		('carol_delete_wallet',      'unloading and deleting Carol’s tracking wallet'),
+		('carol_twimport_pretty',    'importing an edited tracking wallet JSON dump (ignore_checksum=1)'),
+		('carol_listaddresses',      'viewing Carol’s tracking wallet'),
+
 		('bob_split2',               "splitting Bob's funds"),
 		('bob_split2',               "splitting Bob's funds"),
 		('bob_0conf0_getbalance',    "Bob's balance (unconfirmed, minconf=0)"),
 		('bob_0conf0_getbalance',    "Bob's balance (unconfirmed, minconf=0)"),
 		('bob_0conf1_getbalance',    "Bob's balance (unconfirmed, minconf=1)"),
 		('bob_0conf1_getbalance',    "Bob's balance (unconfirmed, minconf=1)"),
@@ -986,12 +993,26 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def bob_twexport_noamt(self):
 	def bob_twexport_noamt(self):
 		return self.bob_twexport(add_args=['include_amts=0'])
 		return self.bob_twexport(add_args=['include_amts=0'])
 
 
-	def carol_twimport(self,add_args=[]):
+	def bob_twexport_pretty(self):
+		return self.bob_twexport(add_args=['pretty=1'])
+
+	def bob_edit_json_twdump(self):
+		self.spawn('',msg_only=True)
+		from mmgen.tw.json import TwJSON
+		fn = TwJSON.Base(self.proto).dump_fn
+		text = json.loads(self.read_from_tmpfile(fn))
+		text['data']['entries'][3][3] = f'edited comment [фубар] [{gr_uc}]'
+		self.write_to_tmpfile( fn, json.dumps(text,indent=4) )
+		return 'ok'
+
+	def carol_twimport(self,add_args=[],expect_str=None):
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
 		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
 		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
 		t = self.spawn('mmgen-tool',['--carol','twimport',fn] + add_args)
 		t = self.spawn('mmgen-tool',['--carol','twimport',fn] + add_args)
 		t.expect('(y/N): ','y')
 		t.expect('(y/N): ','y')
-		if 'batch=true' in add_args:
+		if expect_str:
+			t.expect(expect_str)
+		elif 'batch=true' in add_args:
 			t.expect('{} addresses imported'.format(15 if self.proto.coin == 'BCH' else 25))
 			t.expect('{} addresses imported'.format(15 if self.proto.coin == 'BCH' else 25))
 		else:
 		else:
 			t.expect('import completed OK')
 			t.expect('import completed OK')
@@ -1004,6 +1025,12 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def carol_twimport_batch(self):
 	def carol_twimport_batch(self):
 		return self.carol_twimport(add_args=['batch=true'])
 		return self.carol_twimport(add_args=['batch=true'])
 
 
+	def carol_twimport_pretty(self):
+		return self.carol_twimport(add_args=['ignore_checksum=true'],expect_str='ignoring incorrect checksum')
+
+	def carol_listaddresses(self):
+		return self.spawn('mmgen-tool',['--carol','listaddresses','showempty=1'])
+
 	async def carol_delete_wallet(self):
 	async def carol_delete_wallet(self):
 		imsg(f'Unloading Carol’s tracking wallet')
 		imsg(f'Unloading Carol’s tracking wallet')
 		t = self.spawn('mmgen-regtest',['cli','unloadwallet','carol'])
 		t = self.spawn('mmgen-regtest',['cli','unloadwallet','carol'])