obj.py: move MMGenAddrType and other address-related data objects to addr.py

This commit is contained in:
The MMGen Project 2022-01-15 14:00:09 +00:00
commit 126a09d57d
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
18 changed files with 199 additions and 185 deletions

View file

@ -27,6 +27,158 @@ from .obj import *
from .baseconv import *
from .protocol import hash160
from .key import PrivKey,PubKey
from .seed import SeedID
ati = namedtuple('addrtype_info',
['name','pubkey_type','compressed','gen_method','addr_fmt','wif_label','extra_attrs','desc'])
class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
width = 1
trunc_ok = False
color = 'blue'
name = ImmutableAttr(str)
pubkey_type = ImmutableAttr(str)
compressed = ImmutableAttr(bool,set_none_ok=True)
gen_method = ImmutableAttr(str,set_none_ok=True)
addr_fmt = ImmutableAttr(str,set_none_ok=True)
wif_label = ImmutableAttr(str,set_none_ok=True)
extra_attrs = ImmutableAttr(tuple,set_none_ok=True)
desc = ImmutableAttr(str)
mmtypes = {
'L': ati('legacy', 'std', False,'p2pkh', 'p2pkh', 'wif', (), 'Legacy uncompressed address'),
'C': ati('compressed','std', True, 'p2pkh', 'p2pkh', 'wif', (), 'Compressed P2PKH address'),
'S': ati('segwit', 'std', True, 'segwit', 'p2sh', 'wif', (), 'Segwit P2SH-P2WPKH address'),
'B': ati('bech32', 'std', True, 'bech32', 'bech32', 'wif', (), 'Native Segwit (Bech32) address'),
'E': ati('ethereum', 'std', False,'ethereum','ethereum','privkey', ('wallet_passwd',),'Ethereum address'),
'Z': ati('zcash_z','zcash_z',False,'zcash_z', 'zcash_z', 'wif', ('viewkey',), 'Zcash z-address'),
'M': ati('monero', 'monero', False,'monero', 'monero', 'spendkey',('viewkey','wallet_passwd'),'Monero address'),
}
def __new__(cls,proto,id_str,errmsg=None):
if isinstance(id_str,cls):
return id_str
try:
for k,v in cls.mmtypes.items():
if id_str in (k,v.name):
if id_str == v.name:
id_str = k
me = str.__new__(cls,id_str)
for k in v._fields:
setattr(me,k,getattr(v,k))
if me not in proto.mmtypes + ('P',):
raise ValueError(f'{me.name!r}: invalid address type for {proto.name} protocol')
me.proto = proto
return me
raise ValueError(f'{id_str}: unrecognized address type for protocol {proto.name}')
except Exception as e:
return cls.init_fail( e,
f"{errmsg or ''}{id_str!r}: invalid value for {cls.__name__} ({e!s})",
preformat = True )
@classmethod
def get_names(cls):
return [v.name for v in cls.mmtypes.values()]
class MMGenPasswordType(MMGenAddrType):
mmtypes = {
'P': ati('password', 'password', None, None, None, None, None, 'Password generated from MMGen seed')
}
class AddrIdx(MMGenIdx):
max_digits = 7
def is_addr_idx(s):
return get_obj( AddrIdx, n=s, silent=True, return_bool=True )
class AddrListID(str,Hilite,InitErrors,MMGenObject):
width = 10
trunc_ok = False
color = 'yellow'
def __new__(cls,sid,mmtype):
try:
assert type(sid) == SeedID, f'{sid!r} not a SeedID instance'
if not isinstance(mmtype,(MMGenAddrType,MMGenPasswordType)):
raise ValueError(f'{mmtype!r}: not an instance of MMGenAddrType or MMGenPasswordType')
me = str.__new__(cls,sid+':'+mmtype)
me.sid = sid
me.mmtype = mmtype
return me
except Exception as e:
return cls.init_fail(e, f'sid={sid}, mmtype={mmtype}')
def is_addrlist_id(s):
return get_obj( AddrListID, sid=s, silent=True, return_bool=True )
class MMGenID(str,Hilite,InitErrors,MMGenObject):
color = 'orange'
width = 0
trunc_ok = False
def __new__(cls,proto,id_str):
try:
ss = str(id_str).split(':')
assert len(ss) in (2,3),'not 2 or 3 colon-separated items'
t = proto.addr_type((ss[1],proto.dfl_mmtype)[len(ss)==2])
me = str.__new__(cls,'{}:{}:{}'.format(ss[0],t,ss[-1]))
me.sid = SeedID(sid=ss[0])
me.idx = AddrIdx(ss[-1])
me.mmtype = t
assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}'
me.al_id = str.__new__(AddrListID,me.sid+':'+me.mmtype) # checks already done
me.sort_key = '{}:{}:{:0{w}}'.format(me.sid,me.mmtype,me.idx,w=me.idx.max_digits)
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,id_str)
def is_mmgen_id(proto,s):
return get_obj( MMGenID, proto=proto, id_str=s, silent=True, return_bool=True )
class CoinAddr(str,Hilite,InitErrors,MMGenObject):
color = 'cyan'
hex_width = 40
width = 1
trunc_ok = False
def __new__(cls,proto,addr):
if type(addr) == cls:
return addr
try:
assert set(addr) <= set(ascii_letters+digits),'contains non-alphanumeric characters'
me = str.__new__(cls,addr)
ap = proto.parse_addr(addr)
assert ap, f'coin address {addr!r} could not be parsed'
me.addr_fmt = ap.fmt
me.hex = ap.bytes.hex()
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,addr,objname=f'{proto.cls_name} address')
@classmethod
def fmtc(cls,addr,**kwargs):
w = kwargs['width'] or cls.width
return super().fmtc(addr[:w-2]+'..' if w < len(addr) else addr, **kwargs)
def is_coin_addr(proto,s):
return get_obj( CoinAddr, proto=proto, addr=s, silent=True, return_bool=True )
class TokenAddr(CoinAddr):
color = 'blue'
class ViewKey(object):
def __new__(cls,proto,viewkey):
if proto.name == 'Zcash':
return ZcashViewKey.__new__(ZcashViewKey,proto,viewkey)
elif proto.name == 'Monero':
return MoneroViewKey.__new__(MoneroViewKey,viewkey)
else:
raise ValueError(f'{proto.name}: protocol does not support view keys')
class MoneroViewKey(HexStr):
color,width,hexcase = 'cyan',64,'lower' # FIXME - no checking performed
class ZcashViewKey(CoinAddr):
hex_width = 128
class AddrGenerator(MMGenObject):
def __new__(cls,proto,addr_type):

View file

@ -22,8 +22,8 @@ addrdata.py: MMGen AddrData and related classes
from .util import vmsg,altcoin_subclass
from .base_obj import AsyncInit
from .obj import MMGenObject,MMGenDict,get_obj,AddrListID
from .addr import MMGenID
from .obj import MMGenObject,MMGenDict,get_obj
from .addr import MMGenID,AddrListID
from .addrlist import AddrListEntry,AddrListData,AddrList
class AddrData(MMGenObject):

View file

@ -31,9 +31,10 @@ from .util import (
keypress_confirm,
)
from .protocol import init_proto
from .obj import *
from .obj import MMGenObject,TwComment,WalletPassword,MMGenPWIDString
from .seed import SeedID,is_seed_id
from .key import PrivKey
from .addr import ViewKey,AddrListID,MMGenPasswordType,is_addr_idx
from .addrlist import KeyList,AddrListData,dmsg_sc
from .passwdlist import PasswordList

View file

@ -26,7 +26,7 @@ from .objmethods import MMGenObject,Hilite,InitErrors
from .obj import MMGenListItem,ListItemAttr,MMGenDict,WalletPassword
from .seed import SeedID
from .key import PrivKey
from .obj import MMGenID,MMGenAddrType,CoinAddr,AddrIdx,AddrListID,ViewKey
from .addr import MMGenID,MMGenAddrType,CoinAddr,AddrIdx,AddrListID,ViewKey
def dmsg_sc(desc,data):
from .globalvars import g

View file

@ -23,11 +23,11 @@ altcoins.eth.contract: Ethereum contract and token classes for the MMGen suite
from decimal import Decimal
from . import rlp
from mmgen.util import msg,pp_msg
from mmgen.globalvars import g
from mmgen.common import *
from mmgen.base_obj import AsyncInit
from mmgen.obj import MMGenObject,CoinAddr,TokenAddr,CoinTxID
from mmgen.util import msg
from mmgen.obj import MMGenObject,CoinTxID
from mmgen.addr import CoinAddr,TokenAddr
from .obj import ETHAmt
def parse_abi(s):

View file

@ -20,9 +20,12 @@
altcoins.eth.tw: Ethereum tracking wallet and related classes for the MMGen suite
"""
from mmgen.common import *
from mmgen.obj import TwLabel,is_coin_addr,is_mmgen_id,ListItemAttr,ImmutableAttr
from mmgen.tw import TrackingWallet,TwAddrList,TwUnspentOutputs,TwGetBalance
import os
from mmgen.util import msg,vmsg,ymsg,write_mode
from mmgen.obj import ListItemAttr,ImmutableAttr
from mmgen.tw import TrackingWallet,TwAddrList,TwUnspentOutputs,TwGetBalance,TwLabel
from mmgen.addr import is_coin_addr,is_mmgen_id
from mmgen.addrdata import AddrData,TwAddrData
from .contract import Token,TokenResolve
from .obj import ETHAmt
@ -169,7 +172,7 @@ class EthereumTokenTrackingWallet(EthereumTrackingWallet):
from mmgen.exception import UnrecognizedTokenSymbol
raise UnrecognizedTokenSymbol(f'Specified token {proto.tokensym!r} could not be resolved!')
from mmgen.obj import TokenAddr
from mmgen.addr import TokenAddr
self.token = TokenAddr(proto,token_addr)
if self.token not in self.data['tokens']:
@ -300,7 +303,7 @@ class EthereumTwAddrList(TwAddrList):
tw_dict = self.wallet.mmid_ordered_dict
self.total = self.proto.coin_amt('0')
from mmgen.obj import CoinAddr
from mmgen.addr import CoinAddr
for mmid,d in list(tw_dict.items()):
# if d['confirmations'] < minconf: continue # cannot get confirmations for eth account
label = TwLabel(self.proto,mmid+' '+d['comment'])

View file

@ -21,9 +21,17 @@ altcoins.eth.tx: Ethereum transaction classes for the MMGen suite
"""
import json
from mmgen.common import *
from collections import namedtuple
from decimal import Decimal
from mmgen.globalvars import g
from mmgen.color import red,yellow,blue,pink
from mmgen.opts import opt
from mmgen.util import msg,msg_r,ymsg,dmsg,fmt,line_input,is_int,is_hex_str,make_chksum_6,die,suf,capfirst,pp_fmt
from mmgen.exception import TransactionChainMismatch
from mmgen.obj import *
from mmgen.obj import Int,Str,HexStr,CoinTxID,MMGenTxID
from mmgen.addr import MMGenID,CoinAddr,TokenAddr,is_mmgen_id,is_coin_addr
from mmgen.tx import MMGenTX
from mmgen.tw import TrackingWallet

View file

@ -101,7 +101,7 @@ if opt.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]:
if len(cmd_args) != 2:
die(1,f'This command requires exactly two {g.proj_name} addresses as arguments')
from .obj import MMGenID
from .addr import MMGenID
try:
mmids = [MMGenID(a) for a in cmd_args]
except:

View file

@ -57,18 +57,6 @@ def get_obj(objname,*args,**kwargs):
else:
return True if return_bool else ret
def is_addr_idx(s):
return get_obj(AddrIdx, n=s, silent=True, return_bool=True)
def is_addrlist_id(s):
return get_obj(AddrListID, sid=s, silent=True, return_bool=True)
def is_mmgen_id(proto,s):
return get_obj(MMGenID, proto=proto, id_str=s, silent=True, return_bool=True)
def is_coin_addr(proto,s):
return get_obj(CoinAddr, proto=proto, addr=s, silent=True, return_bool=True)
# dict that keeps a list of keys for efficient lookup by index
class IndexedDict(dict):
@ -268,9 +256,6 @@ class MMGenListItem(MMGenObject):
class MMGenIdx(Int):
min_val = 1
class AddrIdx(MMGenIdx):
max_digits = 7
class MMGenRange(tuple,InitErrors,MMGenObject):
min_idx = None
@ -314,67 +299,6 @@ class MMGenRange(tuple,InitErrors,MMGenObject):
def items(self):
return list(self.iterate())
class CoinAddr(str,Hilite,InitErrors,MMGenObject):
color = 'cyan'
hex_width = 40
width = 1
trunc_ok = False
def __new__(cls,proto,addr):
if type(addr) == cls:
return addr
try:
assert set(addr) <= set(ascii_letters+digits),'contains non-alphanumeric characters'
me = str.__new__(cls,addr)
ap = proto.parse_addr(addr)
assert ap, f'coin address {addr!r} could not be parsed'
me.addr_fmt = ap.fmt
me.hex = ap.bytes.hex()
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,addr,objname=f'{proto.cls_name} address')
@classmethod
def fmtc(cls,addr,**kwargs):
w = kwargs['width'] or cls.width
return super().fmtc(addr[:w-2]+'..' if w < len(addr) else addr, **kwargs)
class TokenAddr(CoinAddr):
color = 'blue'
class ViewKey(object):
def __new__(cls,proto,viewkey):
if proto.name == 'Zcash':
return ZcashViewKey.__new__(ZcashViewKey,proto,viewkey)
elif proto.name == 'Monero':
return MoneroViewKey.__new__(MoneroViewKey,viewkey)
else:
raise ValueError(f'{proto.name}: protocol does not support view keys')
class ZcashViewKey(CoinAddr): hex_width = 128
class MMGenID(str,Hilite,InitErrors,MMGenObject):
color = 'orange'
width = 0
trunc_ok = False
def __new__(cls,proto,id_str):
from .seed import SeedID
try:
ss = str(id_str).split(':')
assert len(ss) in (2,3),'not 2 or 3 colon-separated items'
t = proto.addr_type((ss[1],proto.dfl_mmtype)[len(ss)==2])
me = str.__new__(cls,'{}:{}:{}'.format(ss[0],t,ss[-1]))
me.sid = SeedID(sid=ss[0])
me.idx = AddrIdx(ss[-1])
me.mmtype = t
assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}'
me.al_id = str.__new__(AddrListID,me.sid+':'+me.mmtype) # checks already done
me.sort_key = '{}:{}:{:0{w}}'.format(me.sid,me.mmtype,me.idx,w=me.idx.max_digits)
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,id_str)
class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
color = 'orange'
width = 0
@ -383,6 +307,7 @@ class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
if type(id_str) == cls:
return id_str
ret = None
from .addr import MMGenID
try:
ret = MMGenID(proto,id_str)
sort_key,idtype = ret.sort_key,'mmgen'
@ -449,29 +374,9 @@ class CoinTxID(HexStr):
class WalletPassword(HexStr):
color,width,hexcase = ('blue',32,'lower')
class MoneroViewKey(HexStr):
color,width,hexcase = ('cyan',64,'lower') # FIXME - no checking performed
class MMGenTxID(HexStr):
color,width,hexcase = ('red',6,'upper')
class AddrListID(str,Hilite,InitErrors,MMGenObject):
width = 10
trunc_ok = False
color = 'yellow'
def __new__(cls,sid,mmtype):
from .seed import SeedID
try:
assert type(sid) == SeedID, f'{sid!r} not a SeedID instance'
if not isinstance(mmtype,(MMGenAddrType,MMGenPasswordType)):
raise ValueError(f'{mmtype!r}: not an instance of MMGenAddrType or MMGenPasswordType')
me = str.__new__(cls,sid+':'+mmtype)
me.sid = sid
me.mmtype = mmtype
return me
except Exception as e:
return cls.init_fail(e, f'sid={sid}, mmtype={mmtype}')
class MMGenLabel(str,Hilite,InitErrors):
color = 'pink'
allowed = []
@ -535,60 +440,3 @@ class MMGenPWIDString(MMGenLabel):
desc = 'password ID string'
forbidden = list(' :/\\')
trunc_ok = False
from collections import namedtuple
ati = namedtuple('addrtype_info',
['name','pubkey_type','compressed','gen_method','addr_fmt','wif_label','extra_attrs','desc'])
class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
width = 1
trunc_ok = False
color = 'blue'
name = ImmutableAttr(str)
pubkey_type = ImmutableAttr(str)
compressed = ImmutableAttr(bool,set_none_ok=True)
gen_method = ImmutableAttr(str,set_none_ok=True)
addr_fmt = ImmutableAttr(str,set_none_ok=True)
wif_label = ImmutableAttr(str,set_none_ok=True)
extra_attrs = ImmutableAttr(tuple,set_none_ok=True)
desc = ImmutableAttr(str)
mmtypes = {
'L': ati('legacy', 'std', False,'p2pkh', 'p2pkh', 'wif', (), 'Legacy uncompressed address'),
'C': ati('compressed','std', True, 'p2pkh', 'p2pkh', 'wif', (), 'Compressed P2PKH address'),
'S': ati('segwit', 'std', True, 'segwit', 'p2sh', 'wif', (), 'Segwit P2SH-P2WPKH address'),
'B': ati('bech32', 'std', True, 'bech32', 'bech32', 'wif', (), 'Native Segwit (Bech32) address'),
'E': ati('ethereum', 'std', False,'ethereum','ethereum','privkey', ('wallet_passwd',),'Ethereum address'),
'Z': ati('zcash_z','zcash_z',False,'zcash_z', 'zcash_z', 'wif', ('viewkey',), 'Zcash z-address'),
'M': ati('monero', 'monero', False,'monero', 'monero', 'spendkey',('viewkey','wallet_passwd'),'Monero address'),
}
def __new__(cls,proto,id_str,errmsg=None):
if isinstance(id_str,cls):
return id_str
try:
for k,v in cls.mmtypes.items():
if id_str in (k,v.name):
if id_str == v.name:
id_str = k
me = str.__new__(cls,id_str)
for k in v._fields:
setattr(me,k,getattr(v,k))
if me not in proto.mmtypes + ('P',):
raise ValueError(f'{me.name!r}: invalid address type for {proto.name} protocol')
me.proto = proto
return me
raise ValueError(f'{id_str}: unrecognized address type for protocol {proto.name}')
except Exception as e:
return cls.init_fail( e,
f"{errmsg or ''}{id_str!r}: invalid value for {cls.__name__} ({e!s})",
preformat = True )
@classmethod
def get_names(cls):
return [v.name for v in cls.mmtypes.values()]
class MMGenPasswordType(MMGenAddrType):
mmtypes = {
'P': ati('password', 'password', None, None, None, None, None, 'Password generated from MMGen seed')
}

View file

@ -25,7 +25,6 @@ from collections import namedtuple
from .util import msg,ymsg,Msg,ydie
from .devtools import *
from .obj import CoinAddr,MMGenAddrType
from .globalvars import g
from .amt import BTCAmt,LTCAmt,BCHAmt,XMRAmt
from .altcoins.eth.obj import ETHAmt

View file

@ -230,7 +230,7 @@ def _process_result(ret,pager=False,print_result=False):
else:
ydie(1,f'tool.py: can’t handle return value of type {type(ret).__name__!r}')
from .obj import MMGenAddrType
from .addr import MMGenAddrType
def conv_cls_bip39():
from .bip39 import bip39

View file

@ -47,8 +47,8 @@ from .util import (
)
from .base_obj import AsyncInit
from .objmethods import Hilite,InitErrors,MMGenObject
from .obj import *
from .tx import is_mmgen_id,is_coin_addr
from .obj import ImmutableAttr,ListItemAttr,MMGenListItem,MMGenList,MMGenDict,TwComment,get_obj,TwLabel,TwMMGenID
from .addr import CoinAddr,MMGenID,AddrIdx,is_mmgen_id,is_coin_addr
from .rpc import rpc_init
def get_tw_label(proto,s):
@ -59,7 +59,8 @@ def get_tw_label(proto,s):
return TwLabel(proto,s)
except BadTwComment:
raise
except:
except Exception as e:
print(e)
return None
class TwUnspentOutputs(MMGenObject,metaclass=AsyncInit):
@ -98,8 +99,8 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
amt = ImmutableAttr(None)
amt2 = ListItemAttr(None)
label = ListItemAttr('TwComment',reassign_ok=True)
twmmid = ImmutableAttr('TwMMGenID',include_proto=True)
addr = ImmutableAttr('CoinAddr',include_proto=True)
twmmid = ImmutableAttr(TwMMGenID,include_proto=True)
addr = ImmutableAttr(CoinAddr,include_proto=True)
confs = ImmutableAttr(int,typeconv=False)
date = ListItemAttr(int,typeconv=False,reassign_ok=True)
scriptPubKey = ImmutableAttr('HexStr')

View file

@ -24,6 +24,7 @@ import sys,os,json
from stat import *
from .common import *
from .obj import *
from .addr import MMGenID,CoinAddr,is_mmgen_id,is_coin_addr
wmsg = lambda k: {
'addr_in_addrfile_only': """
@ -210,8 +211,8 @@ class MMGenTxIO(MMGenListItem):
vout = ListItemAttr(int,typeconv=False)
amt = ImmutableAttr(None)
label = ListItemAttr('TwComment',reassign_ok=True)
mmid = ListItemAttr('MMGenID',include_proto=True)
addr = ImmutableAttr('CoinAddr',include_proto=True)
mmid = ListItemAttr(MMGenID,include_proto=True)
addr = ImmutableAttr(CoinAddr,include_proto=True)
confs = ListItemAttr(int) # confs of type long exist in the wild, so convert
txid = ListItemAttr('CoinTxID')
have_wif = ListItemAttr(bool,typeconv=False,delete_ok=True)

View file

@ -21,8 +21,9 @@ txsign: Sign a transaction generated by 'mmgen-txcreate'
"""
from .common import *
from .obj import MMGenList
from .addr import MMGenAddrType
from .addrlist import AddrIdxList,KeyAddrList
from .obj import MMGenAddrType,MMGenList
from .wallet import Wallet,WalletUnenc,WalletEnc,MMGenWallet
from .tx import MMGenTX

View file

@ -24,12 +24,13 @@ import os,re,time,json
from collections import namedtuple
from .common import *
from .objmethods import Hilite,InitErrors
from .rpc import MoneroRPCClientRaw,MoneroWalletRPCClient,json_encoder
from .obj import CoinTxID
from .seed import SeedID
from .daemon import MoneroWalletDaemon
from .protocol import _b58a,init_proto
from .addr import CoinAddr,AddrIdx
from .addrlist import KeyAddrList,AddrIdxList
from .obj import CoinAddr,CoinTxID,AddrIdx
from .rpc import MoneroRPCClientRaw,MoneroWalletRPCClient,json_encoder
from .daemon import MoneroWalletDaemon
xmrwallet_uarg_info = (
lambda e,hp: {

View file

@ -401,9 +401,8 @@ def parse_arg2():
# begin execution
from mmgen.protocol import init_proto
from mmgen.altcoin import CoinInfo as ci
from mmgen.obj import MMGenAddrType
from mmgen.key import PrivKey
from mmgen.addr import KeyGenerator,AddrGenerator
from mmgen.addr import KeyGenerator,AddrGenerator,MMGenAddrType
addr_type = MMGenAddrType(
proto = proto,

View file

@ -186,7 +186,7 @@ if opt.list_names:
die(0,'\n{}\n {}'.format(yellow('Untested commands:'),'\n '.join(uc)))
from mmgen.key import is_wif
from mmgen.tx import is_coin_addr
from mmgen.addr import is_coin_addr
def is_wif_loc(s):
return is_wif(proto,s)
def is_coin_addr_loc(s):

View file

@ -44,7 +44,7 @@ def is_str(s):
return type(s) == str
from mmgen.key import is_wif
from mmgen.obj import is_coin_addr
from mmgen.addr import is_coin_addr
def is_wif_loc(s):
return is_wif(proto,s)
def is_coin_addr_loc(s):