immutable attr improvements

This commit is contained in:
The MMGen Project 2017-08-08 11:31:57 +03:00
commit aae5f122b4
Signed by: mmgen
GPG key ID: 62DBE9E5212F05BE
5 changed files with 58 additions and 69 deletions

View file

@ -91,18 +91,16 @@ class KeyGeneratorSecp256k1(KeyGenerator):
return PubKey(hexlify(priv2pub(unhexlify(privhex),int(privhex.compressed))),compressed=privhex.compressed)
class AddrListEntry(MMGenListItem):
reassign_ok = 'label',
addr = MMGenListItemAttr('addr','BTCAddr')
idx = MMGenListItemAttr('idx','AddrIdx')
label = MMGenListItemAttr('label','TwComment')
sec = MMGenImmutableAttr('sec',PrivKey)
idx = MMGenImmutableAttr('idx','AddrIdx')
label = MMGenListItemAttr('label','TwComment',reassign_ok=True)
sec = MMGenListItemAttr('sec',PrivKey,typeconv=False)
class PasswordListEntry(MMGenListItem):
reassign_ok = 'label',
passwd = MMGenImmutableAttr('passwd',unicode) # TODO: create Password type
idx = MMGenListItemAttr('idx','AddrIdx')
label = MMGenListItemAttr('label','TwComment')
sec = MMGenImmutableAttr('sec',PrivKey)
passwd = MMGenImmutableAttr('passwd',unicode,typeconv=False) # TODO: create Password type
idx = MMGenImmutableAttr('idx','AddrIdx')
label = MMGenListItemAttr('label','TwComment',reassign_ok=True)
sec = MMGenListItemAttr('sec',PrivKey,typeconv=False)
class AddrListChksum(str,Hilite):
color = 'pink'

View file

@ -101,19 +101,13 @@ class MMGenObject(object):
class MMGenList(list,MMGenObject): pass
class MMGenDict(dict,MMGenObject): pass
# for attrs that are always present in the data instance
# reassignment and deletion forbidden
class MMGenImmutableAttr(object): # Descriptor
typeconv = False
builtin_typeconv = False
def __init__(self,name,dtype,typeconv=None,builtin_typeconv=None):
if typeconv is not None:
assert typeconv in (True,False)
self.typeconv = typeconv
if builtin_typeconv is not None:
assert builtin_typeconv
self.builtin_typeconv = builtin_typeconv
self.typeconv = False # override
def __init__(self,name,dtype,typeconv=True):
self.typeconv = typeconv
assert type(dtype) in (str,type)
self.name = name
self.dtype = dtype
@ -121,17 +115,16 @@ class MMGenImmutableAttr(object): # Descriptor
return instance.__dict__[self.name]
# forbid all reassignment
def chk_ok_set_attr(self,instance):
if hasattr(instance,self.name):
m = "Attribute '{}' of {} instance cannot be reassigned"
raise AttributeError(m.format(self.name,type(instance)))
def set_attr_ok(self,instance):
return not hasattr(instance,self.name)
def __set__(self,instance,value):
self.chk_ok_set_attr(instance)
if not self.set_attr_ok(instance):
m = "Attribute '{}' of {} instance cannot be reassigned"
raise AttributeError(m.format(self.name,type(instance)))
if self.typeconv: # convert type
instance.__dict__[self.name] = globals()[self.dtype](value)
elif self.builtin_typeconv:
instance.__dict__[self.name] = self.dtype(value)
instance.__dict__[self.name] = \
globals()[self.dtype](value) if type(self.dtype) == str else self.dtype(value)
else: # check type
if type(value) != self.dtype:
m = "Attribute '{}' of {} instance must of type {}"
@ -139,36 +132,35 @@ class MMGenImmutableAttr(object): # Descriptor
instance.__dict__[self.name] = value
def __delete__(self,instance):
if self.name in instance.delete_ok:
if self.name in instance.__dict__:
del instance.__dict__[self.name]
else:
m = "Atribute '{}' of {} instance cannot be deleted"
raise AttributeError(m.format(self.name,type(instance)))
m = "Atribute '{}' of {} instance cannot be deleted"
raise AttributeError(m.format(self.name,type(instance)))
# for attrs that might not be present in the data instance
# reassignment or deletion allowed if specified
class MMGenListItemAttr(MMGenImmutableAttr):
typeconv = True
builtin_typeconv = False
def __init__(self,name,dtype,typeconv=True,reassign_ok=False,delete_ok=False):
self.reassign_ok = reassign_ok
self.delete_ok = delete_ok
MMGenImmutableAttr.__init__(self,name,dtype,typeconv=typeconv)
# return None if attribute doesn't exist
def __get__(self,instance,owner):
try: return instance.__dict__[self.name]
except: return None
# allow reassignment if value is None or attr in reassign_ok list
def chk_ok_set_attr(self,instance):
if hasattr(instance,self.name) and not (
getattr(instance,self.name) == None or self.name in instance.reassign_ok
):
m = "Attribute '{}' of {} instance cannot be reassigned"
raise AttributeError(m.format(self.name,type(instance)))
def set_attr_ok(self,instance):
return getattr(instance,self.name) == None or self.reassign_ok
def __delete__(self,instance):
if self.delete_ok:
if self.name in instance.__dict__:
del instance.__dict__[self.name]
else:
MMGenImmutableAttr.__delete__(self,instance)
class MMGenListItem(MMGenObject):
reassign_ok = ()
delete_ok = ()
def __init__(self,*args,**kwargs):
if args:
raise ValueError, 'Non-keyword args not allowed'
@ -576,8 +568,8 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
width = 64
trunc_ok = False
compressed = MMGenImmutableAttr('compressed',bool)
wif = MMGenImmutableAttr('wif',WifKey)
compressed = MMGenImmutableAttr('compressed',bool,typeconv=False)
wif = MMGenImmutableAttr('wif',WifKey,typeconv=False)
def __new__(*args,**kwargs): # initialize with (priv_bin,compressed), WIF or self
cls = args[0]

View file

@ -40,17 +40,16 @@ class MMGenTrackingWallet(MMGenObject):
class MMGenTwUnspentOutput(MMGenListItem):
# attrs = 'txid','vout','amt','label','twmmid','addr','confs','scriptPubKey','days','skip'
reassign_ok = 'label','skip'
txid = MMGenListItemAttr('txid','BitcoinTxID')
vout = MMGenListItemAttr('vout',int,typeconv=False),
amt = MMGenListItemAttr('amt','BTCAmt'),
label = MMGenListItemAttr('label','TwComment'),
twmmid = MMGenListItemAttr('twmmid','TwMMGenID')
addr = MMGenListItemAttr('addr','BTCAddr'),
confs = MMGenListItemAttr('confs',int,typeconv=False),
scriptPubKey = MMGenListItemAttr('scriptPubKey','HexStr')
txid = MMGenImmutableAttr('txid','BitcoinTxID')
vout = MMGenImmutableAttr('vout',int,typeconv=False),
amt = MMGenImmutableAttr('amt','BTCAmt'),
label = MMGenListItemAttr('label','TwComment',reassign_ok=True),
twmmid = MMGenImmutableAttr('twmmid','TwMMGenID')
addr = MMGenImmutableAttr('addr','BTCAddr'),
confs = MMGenImmutableAttr('confs',int,typeconv=False),
scriptPubKey = MMGenImmutableAttr('scriptPubKey','HexStr')
days = MMGenListItemAttr('days',int,typeconv=False),
skip = MMGenListItemAttr('skip',bool,typeconv=False),
skip = MMGenListItemAttr('skip',bool,typeconv=False,reassign_ok=True),
wmsg = {
'no_spendable_outputs': """

View file

@ -114,16 +114,14 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
return OrderedDict.__init__(self, ((k,d[k]) for k in keys))
txio_attrs = {
'reassign_ok': ('label',),
'delete_ok': ('have_wif',),
'vout': MMGenListItemAttr('vout',int,typeconv=False),
'amt': MMGenListItemAttr('amt','BTCAmt'),
'label': MMGenListItemAttr('label','TwComment'),
'amt': MMGenImmutableAttr('amt','BTCAmt'),
'label': MMGenListItemAttr('label','TwComment',reassign_ok=True),
'mmid': MMGenListItemAttr('mmid','MMGenID'),
'addr': MMGenListItemAttr('addr','BTCAddr'),
'confs': MMGenListItemAttr('confs',int,builtin_typeconv=True), # long confs found in the wild, so convert
'addr': MMGenImmutableAttr('addr','BTCAddr'),
'confs': MMGenListItemAttr('confs',int,typeconv=True), # long confs exist in the wild, so convert
'txid': MMGenListItemAttr('txid','BitcoinTxID'),
'have_wif': MMGenListItemAttr('have_wif',bool,typeconv=False)
'have_wif': MMGenListItemAttr('have_wif',bool,typeconv=False,delete_ok=True)
}
class MMGenTX(MMGenObject):

View file

@ -235,13 +235,15 @@ class MMGenToolTestSuite(object):
die(1,red('Called process returned with an error (retcode %s)' % retcode))
return (a,a.rstrip())[bool(strip)]
def run_cmd_chk(self,name,f1,f2,kwargs='',extra_msg=''):
def run_cmd_chk(self,name,f1,f2,kwargs='',extra_msg='',strip_hex=False):
idata = read_from_file(f1).rstrip()
odata = read_from_file(f2).rstrip()
ret = self.run_cmd(name,[odata],kwargs=kwargs,extra_msg=extra_msg)
vmsg('In: ' + repr(odata))
vmsg('Out: ' + repr(ret))
if ret == idata: ok()
def cmp_equal(a,b):
return (a.lstrip('0') == b.lstrip('0')) if strip_hex else (a == b)
if cmp_equal(ret,idata): ok()
else:
die(3,red(
"Error: values don't match:\nIn: %s\nOut: %s" % (repr(idata),repr(ret))))
@ -284,12 +286,12 @@ class MMGenToolTestSuite(object):
def Strtob58(self,name): self.run_cmd_out(name,getrandstr(16))
def B58tostr(self,name,f1,f2): self.run_cmd_chk(name,f1,f2)
def Hextob58(self,name): self.run_cmd_out(name,getrandhex(32))
def B58tohex(self,name,f1,f2): self.run_cmd_chk(name,f1,f2)
def B58tohex(self,name,f1,f2): self.run_cmd_chk(name,f1,f2,strip_hex=True)
def B58randenc(self,name):
ret = self.run_cmd_out(name,Return=True)
ok_or_die(ret,is_b58_str,'base 58 string')
def Hextob32(self,name): self.run_cmd_out(name,getrandhex(24))
def B32tohex(self,name,f1,f2): self.run_cmd_chk(name,f1,f2)
def B32tohex(self,name,f1,f2): self.run_cmd_chk(name,f1,f2,strip_hex=True)
def Randhex(self,name):
ret = self.run_cmd_out(name,Return=True)
ok_or_die(ret,binascii.unhexlify,'hex string')