MMGenListItem: require all immutable attrs to be set in constructor

This commit is contained in:
The MMGen Project 2019-10-19 14:28:46 +00:00
commit 514278d616
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 54 additions and 8 deletions

View file

@ -300,7 +300,7 @@ class AddrListEntry(MMGenListItem):
wallet_passwd = MMGenListItemAttr('wallet_passwd','WalletPassword')
class PasswordListEntry(MMGenListItem):
passwd = MMGenImmutableAttr('passwd',str,typeconv=False) # TODO: create Password type
passwd = MMGenListItemAttr('passwd',str,typeconv=False) # TODO: create Password type
idx = MMGenImmutableAttr('idx','AddrIdx')
label = MMGenListItemAttr('label','TwComment',reassign_ok=True)
sec = MMGenListItemAttr('sec',PrivKey,typeconv=False)

View file

@ -21,7 +21,7 @@ altcoins.eth.tw: Ethereum tracking wallet and related classes for the MMGen suit
"""
from mmgen.common import *
from mmgen.obj import ETHAmt,TwLabel,is_coin_addr,is_mmgen_id
from mmgen.obj import ETHAmt,TwLabel,is_coin_addr,is_mmgen_id,MMGenListItem,MMGenListItemAttr,MMGenImmutableAttr
from mmgen.tw import TrackingWallet,TwAddrList,TwUnspentOutputs
from mmgen.addr import AddrData
from .contract import Token
@ -295,6 +295,18 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view,
'confirmations': 0, # TODO
} for d in wl]
class MMGenTwUnspentOutput(MMGenListItem):
txid = MMGenListItemAttr('txid','CoinTxID')
vout = MMGenListItemAttr('vout',int,typeconv=False)
amt = MMGenImmutableAttr('amt',g.proto.coin_amt.__name__)
amt2 = MMGenListItemAttr('amt2',g.proto.coin_amt.__name__)
label = MMGenListItemAttr('label','TwComment',reassign_ok=True)
twmmid = MMGenImmutableAttr('twmmid','TwMMGenID')
addr = MMGenImmutableAttr('addr','CoinAddr')
confs = MMGenImmutableAttr('confs',int,typeconv=False)
days = MMGenListItemAttr('days',int,typeconv=False)
skip = MMGenListItemAttr('skip',str,typeconv=False,reassign_ok=True)
class EthereumTokenTwUnspentOutputs(EthereumTwUnspentOutputs):
disp_type = 'token'

View file

@ -2,6 +2,7 @@
class MMGenObject(object):
'placeholder - overridden when testing'
def immutable_attr_init_check(self): pass
import os
if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN_TRACEBACK'):
@ -107,6 +108,24 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
import re
return re.sub('\n+','\n',''.join(out))
# Check that all immutables have been initialized. Expensive, so do only when testing.
def immutable_attr_init_check(self):
from mmgen.globalvars import g
if g.test_suite:
from mmgen.util import rdie
cls = type(self)
for attrname in sorted({a for a in self.valid_attrs if a[0] != '_'}):
for o in (cls,cls.__bases__[0]): # assume there's only one base class
if attrname in o.__dict__:
attr = o.__dict__[attrname]
break
else:
rdie(3,'unable to find descriptor {}.{}'.format(cls.__name__,attrname))
if type(attr).__name__ == 'MMGenImmutableAttr':
if attrname not in self.__dict__:
fs = 'attribute {!r} of {} has not been initialized in constructor!'
rdie(3,fs.format(attrname,cls.__name__))
def print_diff(a,b,from_json=True):
if from_json:
a = json.dumps(json.loads(a),indent=4).split('\n') if a else []

View file

@ -251,18 +251,31 @@ class MMGenListItem(MMGenObject):
valid_attrs = None
valid_attrs_extra = set()
invalid_attrs = {
'pfmt',
'pmsg',
'pdie',
'valid_attrs',
'valid_attrs_extra',
'invalid_attrs',
'immutable_attr_init_check',
}
def __init__(self,*args,**kwargs):
if self.valid_attrs == None:
type(self).valid_attrs = (
( {e for e in dir(self) if e[:2] != '__'} | self.valid_attrs_extra ) -
{'pfmt','pmsg','pdie','valid_attrs','valid_attrs_extra'} )
( {e for e in dir(self) if e[:2] != '__'} | self.valid_attrs_extra ) - self.invalid_attrs )
if args:
raise ValueError('Non-keyword args not allowed')
raise ValueError('Non-keyword args not allowed in {!r} constructor'.format(type(self).__name__))
for k in kwargs:
if kwargs[k] != None:
setattr(self,k,kwargs[k])
# Require all immutables to be initialized. Check performed only when testing.
self.immutable_attr_init_check()
# allow only valid attributes to be set
def __setattr__(self,name,value):
if name not in self.valid_attrs:

View file

@ -247,6 +247,8 @@ class MMGenTxIO(MMGenListItem):
class MMGenTxInput(MMGenTxIO):
scriptPubKey = MMGenListItemAttr('scriptPubKey','HexStr')
sequence = MMGenListItemAttr('sequence',int,typeconv=False)
# required by copy_inputs_from_tw()
copy_attrs = { 'scriptPubKey','vout','amt','label','mmid','addr','confs','txid' }
class MMGenTxOutput(MMGenTxIO):
is_chg = MMGenListItemAttr('is_chg',bool,typeconv=False)
@ -660,10 +662,10 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
# inputs methods
def copy_inputs_from_tw(self,tw_unspent_data):
self.inputs = MMGenTxInputList()
MMGenTxInput() # throwaway instance to initialize cls.valid_attrs
for d in tw_unspent_data:
t = MMGenTxInput(**{attr:getattr(d,attr) for attr in d.__dict__ if attr in MMGenTxInput.valid_attrs})
if d.twmmid.type == 'mmgen': t.mmid = d.twmmid # twmmid -> mmid
t = MMGenTxInput(**{attr:getattr(d,attr) for attr in d.__dict__ if attr in MMGenTxInput.copy_attrs})
if d.twmmid.type == 'mmgen':
t.mmid = d.twmmid # twmmid -> mmid
self.inputs.append(t)
def get_input_sids(self):