mmgen-tool twimport,twexport: support Ethereum

This commit is contained in:
The MMGen Project 2022-06-11 16:11:02 +00:00
commit 72aef8b02f
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 209 additions and 4 deletions

View file

@ -91,6 +91,9 @@ class EthereumTrackingWallet(TrackingWallet):
async def batch_import_address(self,args_list):
return [await self.import_address(*a) for a in args_list]
async def rescan_addresses(self,coin_addrs):
pass
@write_mode
async def import_address(self,addr,label):
r = self.data_root
@ -229,6 +232,3 @@ class EthereumTokenTrackingWallet(EthereumTrackingWallet):
'decimals': t.decimals
}
}
async def twimport_check_and_create_wallet(self,info_msg):
raise NotImplementedError('method not implemented for Ethereum')

View file

@ -0,0 +1,145 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen
# https://gitlab.com/mmgen/mmgen
"""
base_proto.ethereum.tw.json: export and import tracking wallet to JSON format
"""
from collections import namedtuple
from ....tw.json import TwJSON
from ....tw.common import TwMMGenID
class EthereumTwJSON(TwJSON):
class Base(TwJSON.Base):
def __init__(self,proto,*args,**kwargs):
self.params_keys = ['symbol','decimals']
self.params_tuple = namedtuple('params_tuple',self.params_keys)
super().__init__(proto,*args,**kwargs)
@property
def mappings_json(self):
def gen_mappings(data):
for d in data:
yield (d.mmgen_id,d.address) if hasattr(d,'mmgen_id') else d
return self.json_dump({
'accounts': list(gen_mappings(self.entries['accounts'])),
'tokens': {k:list(gen_mappings(v)) for k,v in self.entries['tokens'].items()}
})
@property
def num_entries(self):
return len(self.entries['accounts']) + len(self.entries['tokens'])
class Import(TwJSON.Import,Base):
info_msg = """
This utility will recreate a new tracking wallet from the supplied JSON dump.
If the dump contains address balances, balances will be updated from it.
"""
@property
async def tracking_wallet_exists(self):
return bool(self.tw.data['accounts'] or self.tw.data['tokens'])
async def create_tracking_wallet(self):
return True
async def get_entries(self):
edata = self.data['data']['entries']
def gen_entries(data):
for d in data:
if len(d) == 2:
yield self.params_tuple(*d)
else:
e = self.entry_tuple_in(*d)
yield self.entry_tuple(
TwMMGenID(self.proto,e.mmgen_id),
e.address,
getattr(e,'amount','0'),
e.comment )
def gen_token_entries():
for token_addr,token_data in edata['tokens'].items():
yield (
token_addr,
list(gen_entries(token_data)),
)
return {
'accounts': list(gen_entries( edata['accounts'] )),
'tokens': dict(list(gen_token_entries()))
}
async def do_import(self,batch):
def gen_data(data):
for d in data:
if hasattr(d,'address'):
if d.amount is None: # Python 3.9: {} | {}
yield (d.address, {'mmid':d.mmgen_id,'comment':d.comment})
else:
yield (d.address, {'mmid':d.mmgen_id,'comment':d.comment,'balance':d.amount})
else:
yield ('params', {'symbol':d.symbol,'decimals':d.decimals})
self.tw.data = { # keys must be in correct order
'coin': self.coin.upper(),
'network': self.network.upper(),
'accounts': dict(gen_data(self.entries['accounts'])),
'tokens': {k:dict(gen_data(v)) for k,v in self.entries['tokens'].items()},
}
self.tw.write(quiet=False)
class Export(TwJSON.Export,Base):
async def get_entries(self,include_amts=True):
def gen_data(data):
for k,v in data.items():
if k == 'params':
yield self.params_tuple(**v)
elif include_amts:
yield self.entry_tuple(TwMMGenID(self.proto,v['mmid']), k, v.get('balance'), v['comment'])
else:
yield self.entry_tuple_in(TwMMGenID(self.proto,v['mmid']), k, v['comment'])
def gen_token_data():
for token_addr,token_data in self.tw.data['tokens'].items():
yield (
token_addr,
sorted(
gen_data(token_data),
key = lambda x: x.mmgen_id.sort_key if hasattr(x,'mmgen_id') else '+'
)
)
return {
'accounts': sorted(
gen_data(self.tw.data['accounts']),
key = lambda x: x.mmgen_id.sort_key ),
'tokens': dict(sorted(gen_token_data()))
}
@property
async def entries_out(self):
return await self.get_entries(include_amts='amount' in self.keys)
@property
async def total(self):
from ....amt import ETHAmt
return sum(ETHAmt(i.amount) for i in self.entries['accounts']) or ETHAmt('0')

View file

@ -1 +1 @@
13.2.dev6
13.2.dev7

View file

@ -307,6 +307,18 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
('token_remove_addr1', f'removing addr #{del_addrs[0]} from {coin} token tracking wallet'),
('token_remove_addr2', f'removing addr #{del_addrs[1]} from {coin} token tracking wallet'),
('twexport_noamt', 'exporting the tracking wallet (include_amts=0)'),
('twmove', 'moving the tracking wallet'),
('twimport', 'importing the tracking wallet'),
('twview7', 'twview (cached_balances=1)'),
('twview8', 'twview'),
('twexport', 'exporting the tracking wallet'),
('tw_chktotal', 'checking total value in tracking wallet dump'),
('twmove', 'moving the tracking wallet'),
('twimport', 'importing the tracking wallet'),
('twcompare', 'comparing imported tracking wallet with original'),
('twview9', 'twview (check balance)'),
('stop', 'stopping daemon'),
)
@ -1205,6 +1217,12 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
return self.twview(tool_args=['wide=1','minconf=0'])
def twview6(self):
return self.twview(expect_str=vbal6)
def twview7(self):
return self.twview(args=['--cached-balances'])
def twview8(self):
return self.twview()
def twview9(self):
return self.twview(args=['--cached-balances'],expect_str=vbal6)
def token_twview1(self):
return self.twview(args=['--token=mm1'])
@ -1248,6 +1266,48 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
def token_remove_addr2(self):
return self.edit_label(out_num=del_addrs[1],args=['--token=mm1'],action='D')
def twexport_noamt(self):
return self.twexport(add_args=['include_amts=0'])
def twexport(self,add_args=[]):
t = self.spawn('mmgen-tool', self.eth_args + ['twexport'] + add_args)
t.written_to_file('JSON data')
return t
async def twmove(self):
self.spawn('',msg_only=True)
from mmgen.tw.ctl import TrackingWallet
tw = await TrackingWallet(self.proto)
imsg(f'Moving tracking wallet')
os.rename( tw.tw_fn, tw.tw_fn+'.bak.json' )
return 'ok'
def twimport(self,add_args=[]):
from mmgen.tw.json import TwJSON
fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
t = self.spawn('mmgen-tool',self.eth_args + ['twimport',fn] + add_args)
t.expect('(y/N): ','y')
t.written_to_file('tracking wallet data')
return t
def tw_chktotal(self):
self.spawn('',msg_only=True)
from mmgen.tw.json import TwJSON
fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
res = json.loads(read_from_file(fn))
cmp_or_die( res['data']['value'], vbal6, 'value in tracking wallet JSON dump' )
return 'ok'
async def twcompare(self):
self.spawn('',msg_only=True)
from mmgen.tw.ctl import TrackingWallet
tw = await TrackingWallet(self.proto)
fn = tw.tw_fn
imsg(f'Comparing imported tracking wallet with original')
data = [json.dumps(json.loads(read_from_file(f)),sort_keys=True) for f in (fn,fn+'.bak.json')]
cmp_or_die(*data,'tracking wallets')
return 'ok'
def stop(self):
self.spawn('',msg_only=True)
if not opt.no_daemon_stop: