add state invariant tags for account debits

This commit is contained in:
Jude Nelson
2018-03-22 19:19:59 -04:00
parent 2c15d29f03
commit 32552487e0

View File

@@ -22,6 +22,7 @@
"""
import sys
import os
from ..config import OPCODE_CREATION_OPS, OPCODE_TRANSITION_OPS, op_get_opcode_name
@@ -68,16 +69,24 @@ NAMEREC_MUTATE_FIELDS = [
]
def state_preorder_invariant_tags():
"""
Get a list of state-preorder invariant tags
"""
return [
'__account_payment_info__'
]
def state_create_invariant_tags():
"""
Get a list of state-create invariant tags.
"""
return [
'__preorder__',
# '__prior_history__',
'__table__',
'__history_id_key__',
'__state_create__'
'__state_create__',
]
@@ -125,6 +134,11 @@ def state_preorder(collision_checker):
# no collision
rc = True
# sanity check---we need to have the appropriate metadata for this operation
invariant_tags = state_preorder_invariant_tags()
for tag in invariant_tags:
assert tag in nameop, "BUG: missing invariant tag '%s'" % tag
return rc
return wrapped_check
return wrap
@@ -136,7 +150,6 @@ def state_create(history_id_key, table_name, collision_checker, always_set=[]):
Decorator for the check() method on state-creating operations.
Makes sure that:
* there is a __preorder__ field set, which contains the state-creating operation's associated preorder
# * there is a __prior_history__ field set, which contains the state-creating operation's associated prior state history
* there is a __table__ field set, which contains the table into which to insert this state into
* there is a __history_id_key__ field set, which identifies the table's primary key name
* there are no unexpired, duplicate instances of this state with this history id.
@@ -150,14 +163,13 @@ def state_create(history_id_key, table_name, collision_checker, always_set=[]):
# succeeded, and still a state-creating operation?
if rc and op_get_opcode_name( nameop['op'] ) in OPCODE_CREATION_OPS:
# ensure that there's now a __preorder__ # and a __prior_history__
# ensure that there's now a __preorder__
try:
assert '__preorder__' in nameop.keys(), "Missing __preorder__"
# assert '__prior_history__' in nameop.keys(), "Missing __prior_history__"
except Exception, e:
log.exception(e)
log.error("FATAL: missing fields")
sys.exit(1)
os.abort()
# propagate __table__ and __history_id_key__
nameop['__table__'] = table_name
@@ -165,7 +177,7 @@ def state_create(history_id_key, table_name, collision_checker, always_set=[]):
nameop['__state_create__'] = True
nameop['__always_set__'] = always_set
# sanity check
# sanity check---we need to have the appropriate metadata for this operation
invariant_tags = state_create_invariant_tags()
for tag in invariant_tags:
assert tag in nameop, "BUG: missing invariant tag '%s'" % tag
@@ -193,12 +205,12 @@ def state_transition_invariant_tags():
'__table__',
'__history_id_key__',
'__state_transition__',
'__always_set__'
'__always_set__',
]
# sanity check decorator for state-transition operations
def state_transition(history_id_key, table_name, always_set=[]):
def state_transition(history_id_key, table_name, always_set=[], may_spend_tokens=False):
"""
Decorator for the check() method on state-transition operations.
Make sure that:
@@ -220,6 +232,12 @@ def state_transition(history_id_key, table_name, always_set=[]):
nameop['__state_transition__'] = True
nameop['__always_set__'] = always_set
if not may_spend_tokens:
state_transition_put_account_payment_info(nameop, None, None, None)
elif '__account_payment_info__' not in nameop:
raise Exception('Operation spends tokens, but no payment account information is set')
# sanity check
invariant_tags = state_transition_invariant_tags()
for tag in invariant_tags:
@@ -234,12 +252,35 @@ def get_state_invariant_tags():
"""
Get the set of state invariant tags for a given opcode
"""
return list(set( state_create_invariant_tags() + state_transition_invariant_tags() ))
return list(set( state_create_invariant_tags() + state_transition_invariant_tags() + state_preorder_invariant_tags() ))
def state_preorder_get_account_payment_info( nameop ):
"""
Get the payment information for an account. Can be None if no account payments are needed
"""
return nameop['__account_payment_info__']
def state_preorder_put_account_payment_info( nameop, account_addr, token_type, amount ):
"""
Call this in a @state_create-decorated method.
Identifies the account that must be debited.
"""
assert amount is None or isinstance(amount, int)
assert account_addr is None or isinstance(account_addr, (str,unicode))
assert token_type is None or isinstance(token_type, (str,unicode))
nameop['__account_payment_info__'] = {
'address': str(account_addr) if account_addr is not None else None,
'type': str(token_type) if token_type is not None else None,
'amount': int(amount) if amount is not None else None
}
def state_create_put_preorder( nameop, preorder ):
"""
Call this in a @state_create-decorated method.
Identifies the preorder record for this state.
"""
nameop['__preorder__'] = preorder
@@ -251,7 +292,6 @@ def state_create_is_valid( nameop ):
assert '__state_create__' in nameop, "Not tagged with @state_create"
assert nameop['__state_create__'], "BUG: tagged False by @state_create"
assert '__preorder__' in nameop, "No preorder"
# assert '__prior_history__' in nameop, "No prior history"
assert '__table__' in nameop, "No table given"
assert '__history_id_key__' in nameop, "No history ID key given"
assert nameop['__history_id_key__'] in nameop, "No history ID given"
@@ -299,10 +339,26 @@ def state_transition_is_valid( nameop ):
assert history_id_key in ["name", "namespace_id"], "Invalid history ID key '%s'" % history_id_key
assert '__table__' in nameop, "Missing __table__"
assert '__always_set__' in nameop, "No always-set fields given"
assert '__account_payment_info__' in nameop, 'No account payment information present'
return True
def state_transition_put_account_payment_info( nameop, account_addr, token_type, amount ):
"""
Call this in a @state_create-decorated method.
Identifies the account that must be debited.
"""
assert amount is None or isinstance(amount, int)
assert account_addr is None or isinstance(account_addr, (str,unicode))
assert token_type is None or isinstance(token_type, (str,unicode))
nameop['__account_payment_info__'] = {
'address': str(account_addr) if account_addr is not None else None,
'type': str(token_type) if token_type is not None else None,
'amount': int(amount) if amount is not None else None
}
def state_transition_get_table( nameop ):
"""
Get the table of a state-transition operation
@@ -310,6 +366,13 @@ def state_transition_get_table( nameop ):
return nameop['__table__']
def state_transition_get_account_payment_info( nameop ):
"""
Get the payment information for an account. Can be None if no account payments are needed
"""
return nameop['__account_payment_info__']
def state_transition_get_history_id_key( nameop ):
"""
Get the key of the history ID of a state-transition name operation