From 867834b40988fe71e5fffa801d7e39937bf7eb4e Mon Sep 17 00:00:00 2001 From: Jude Nelson Date: Thu, 22 Mar 2018 19:21:25 -0400 Subject: [PATCH] ensure that a namespace preorder paid the right amount of STACKs when it is revealed (but only if we're in the STACKs epochs) --- blockstack/lib/operations/namespacereveal.py | 90 +++++++++++++++----- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/blockstack/lib/operations/namespacereveal.py b/blockstack/lib/operations/namespacereveal.py index b56147674..7123b176a 100644 --- a/blockstack/lib/operations/namespacereveal.py +++ b/blockstack/lib/operations/namespacereveal.py @@ -25,7 +25,7 @@ from ..config import * from ..hashing import * from ..scripts import * from ..nameset import * - +import traceback from binascii import hexlify, unhexlify import json @@ -61,7 +61,7 @@ FIELDS = [ # fields this operation changes # everything but the block number -MUTATE_FIELDS = filter( lambda f: f not in ["block_number"], FIELDS ) +MUTATE_FIELDS = filter( lambda f: f not in ["block_number"], FIELDS ) + ['token_fee'] def namespacereveal_sanity_check( namespace_id, version, lifetime, coeff, base, bucket_exponents, nonalpha_discount, no_vowel_discount ): """ @@ -76,7 +76,7 @@ def namespacereveal_sanity_check( namespace_id, version, lifetime, coeff, base, if len(namespace_id) > LENGTHS['blockchain_id_namespace_id']: raise Exception("Invalid namespace ID length for '%s' (expected length between 1 and %s)" % (namespace_id, LENGTHS['blockchain_id_namespace_id'])) - if version not in [NAMESPACE_VERSION_PAY_TO_BURN, NAMESPACE_VERSION_PAY_TO_CREATOR]: + if version not in [NAMESPACE_VERSION_PAY_TO_BURN, NAMESPACE_VERSION_PAY_TO_CREATOR, NAMESPACE_VERSION_PAY_WITH_STACKS]: raise Exception("Invalid namespace version bits {:x}".format(version)) if lifetime < 0 or lifetime > (2**32 - 1): @@ -171,7 +171,26 @@ def check( state_engine, nameop, block_id, checked_ops ): # must be a version we support # pre F-day 2017: only support names that send payment to the burn address # post F-day 2017: support both pay-to-burn and pay-to-creator + # 2018 phase 1: support paying for names with STACKs tokens namespace_version_bits = int(nameop['version']) + if namespace_version_bits == NAMESPACE_VERSION_PAY_TO_CREATOR: + # need to be in post F-day 2017 or later + if EPOCH_FEATURE_NAMESPACE_BURN_TO_CREATOR not in epoch_features: + log.debug("pay-to-creator is not supported in this epoch") + return False + + elif namespace_version_bits == NAMESPACE_VERSION_PAY_WITH_STACKS: + # need to be in 2018 phase 1 or later + if EPOCH_FEATURE_NAMESPACE_PAY_WITH_STACKS not in epoch_features: + log.debug("pay-with-STACKs-tokens is not supported in this epoch") + return False + + elif namespace_version_bits != NAMESPACE_VERSION_PAY_TO_BURN: + # not supported at all + log.debug("Unsupported namespace version {:x}".format(namespace_version_bits)) + return False + + ''' if EPOCH_FEATURE_NAMESPACE_BURN_TO_CREATOR in epoch_features: # post F-day 2017 # can send to burn address or namespace preorder address @@ -185,19 +204,54 @@ def check( state_engine, nameop, block_id, checked_ops ): if namespace_version_bits != NAMESPACE_VERSION_PAY_TO_BURN: log.debug("Namespace '%s' requires version %s, but this blockstack is version %s" % (namespace_id, nameop['version'], NAMESPACE_VERSION_PAY_TO_BURN)) return False + ''' + units = get_epoch_namespace_price_units(block_id) + tokens_paid = 0 - # check fee... - if not 'op_fee' in namespace_preorder: - log.debug("Namespace '%s' preorder did not pay the fee" % (namespace_id)) - return False + if units == 'STACKS': + if EPOCH_FEATURE_STACKS_BUY_NAMESPACES not in epoch_features: + traceback.print_stack() + log.fatal("Namespaces must be bought in STACKs, but this epoch does not support it!") + os.abort() - namespace_fee = namespace_preorder['op_fee'] + # must buy namespaces with STACKs + # how much did the NAMESPACE_PREORDER pay? + if not 'token_fee' in namespace_preorder: + log.debug("Namespace {} did not pay the token fee".format(namespace_id)) + return False - # must have paid enough - if namespace_fee < price_namespace( namespace_id, block_id ): - # not enough money - log.debug("Namespace '%s' costs %s, but sender paid %s" % (namespace_id, price_namespace(namespace_id, block_id), namespace_fee )) - return False + tokens_paid = namespace_preorder['token_fee'] + assert isinstance(tokens_paid, (int,long)) + + token_namespace_fee = price_namespace(namespace_id, block_id, units) + if token_namespace_fee is None: + log.debug("Invalid namespace ID {}".format(namespace_id)) + return False + + if tokens_paid < token_namespace_fee: + # not enough! + log.debug("Namespace buyer {} has {} tokens, but '{}' costs {} tokens".format(token_address, account_balance, token_name_fee)) + return False + + elif units == 'BTC': + # must buy namespaces with Bitcoin + # check fee... + if not 'op_fee' in namespace_preorder: + log.debug("Namespace '%s' preorder did not pay the fee" % (namespace_id)) + return False + + namespace_fee = namespace_preorder['op_fee'] + + # must have paid enough + if namespace_fee < price_namespace(namespace_id, block_id, units): + # not enough money + log.debug("Namespace '%s' costs %s, but sender paid %s" % (namespace_id, price_namespace(namespace_id, block_id), namespace_fee )) + return False + + else: + traceback.print_stack() + log.fatal("Unknown payment unit {}".format(units)) + os.abort() # is this the first time this namespace has been revealed? old_namespace = state_engine.get_namespace_op_state( namespace_id, block_id, include_expired=True ) @@ -208,7 +262,6 @@ def check( state_engine, nameop, block_id, checked_ops ): # revealed for the first time log.debug("Revealing for the first time: '%s'" % namespace_id) namespace_block_number = namespace_preorder['block_number'] - # state_create_put_prior_history( nameop, None ) else: # revealed for the 2nd or later time @@ -217,19 +270,16 @@ def check( state_engine, nameop, block_id, checked_ops ): # push back preorder block number to the original preorder namespace_block_number = old_namespace['block_number'] - # re-revealing - # prior_hist = prior_history_create( nameop, old_namespace, preorder_block_number, state_engine, extra_backup_fields=['consensus_hash','preorder_hash']) - # state_create_put_prior_history( nameop, prior_hist ) - # record preorder - nameop['block_number'] = namespace_block_number # namespace_preorder['block_number'] + nameop['block_number'] = namespace_block_number nameop['reveal_block'] = block_id state_create_put_preorder( nameop, namespace_preorder ) # NOTE: not fed into the consensus hash, but necessary for database constraints: nameop['ready_block'] = 0 nameop['op_fee'] = namespace_preorder['op_fee'] - + + nameop['token_fee'] = '{}'.format(tokens_paid) # NOTE: avoids overflow # can begin import return True