def check_register(state_engine, nameop, block_id, checked_ops): """ Verify the validity of a registration nameop. * the name must be well-formed * the namespace must be ready * the name does not collide * the name was preordered by the same sender as the last preorder * the Bitcoin or Stacks fee paid by the preorder must be high enough (for some namespace-version-specific definition of "high enough") NAME_REGISTRATION is not allowed during a namespace import, so the namespace must be ready. Return True if accepted. Return False if not. """ from ..nameset import BlockstackDB name = nameop['name'] sender = nameop['sender'] # address mixed into the preorder register_addr = nameop.get('recipient_address', None) if register_addr is None: log.warning("No registration address given") return False recipient = nameop.get('recipient', None) if recipient is None: log.warning("No recipient script given") return False # name must be well-formed if not is_name_valid(name): log.warning("Malformed name '%s'" % name) return False epoch_features = get_epoch_features(block_id) name_fee = None namespace = None preorder_hash = None preorder_block_number = None name_block_number = None consensus_hash = None fee_block_id = None # block ID at which the fee was paid fee_vtxindex = None # vtxindex at which the fee was paid burn_address = None # preorder/renew burn address token_address = None # if we're paying in tokens, this is the token account to debit opcode = nameop['opcode'] first_registered = nameop['first_registered'] # name must be well-formed if not is_b40(name) or "+" in name or name.count(".") > 1: log.warning("Malformed name '%s': non-base-38 characters" % name) return False # name must not be revoked if state_engine.is_name_revoked(name): log.warning("Name '%s' is revoked" % name) return False namespace_id = get_namespace_from_name(name) # namespace must exist and be ready if not state_engine.is_namespace_ready(namespace_id): log.warning("Namespace '%s' is not ready" % namespace_id) return False # get namespace... namespace = state_engine.get_namespace(namespace_id) # cannot exceed quota num_names = get_num_names_owned(state_engine, checked_ops, recipient) if num_names >= MAX_NAMES_PER_SENDER: log.warning("Recipient '%s' has exceeded quota" % recipient) return False # if multisig is not enabled in this epoch, and the recipient # address is a p2sh address, then reject the transaction. # this if for compatibility with 0.13 if virtualchain.is_multisig_address( register_addr) and not epoch_has_multisig(block_id): log.warning( "Multisig registration address %s, but this epoch (%s) does not support multisig" % (register_addr, get_epoch_number(block_id))) return False # get preorder... preorder = state_engine.get_name_preorder(name, sender, register_addr) old_name_rec = state_engine.get_name(name, include_expired=True) if preorder is None: # not preordered log.warning("Name '%s' does not exist, or is not preordered by %s" % (name, sender)) return False # bugfix? if EPOCH_FEATURE_FIX_PREORDER_EXPIRE in epoch_features: # preorder must not be expired if preorder['block_number'] + NAME_PREORDER_EXPIRE < block_id: log.warning("Preorder {} is expired".format( preorder['preorder_hash'])) return False # can't be registered already if state_engine.is_name_registered(name): log.warning("Name '%s' is already registered" % name) return False # name can't be registered if it was reordered before its namespace was ready if not namespace.has_key('ready_block') or preorder[ 'block_number'] < namespace['ready_block']: log.warning("Name '%s' preordered before namespace '%s' was ready" % (name, namespace_id)) return False # name must be preordered by the same sender if preorder['sender'] != sender: log.warning("Name '%s' was not preordered by %s" % (name, sender)) return False # fee was included in the preorder (even if it's just dust) if not 'op_fee' in preorder: log.warning("Name '%s' preorder did not pay the fee" % (name)) return False name_fee = preorder['op_fee'] preorder_hash = preorder['preorder_hash'] preorder_block_number = preorder['block_number'] fee_block_id = preorder_block_number fee_vtxindex = preorder['vtxindex'] burn_address = preorder['burn_address'] token_address = preorder[ 'address'] # note that the *preorderer* pays for a registration in tokens, just as it is with BTC # pass along the preorder state_create_put_preorder(nameop, preorder) if old_name_rec is None: # Case 1(a): registered for the first time ever log.debug("Registering name '%s'" % name) name_block_number = preorder['block_number'] else: # Case 1(b): name expired, and is now re-registered log.debug("Re-registering name '%s'" % name) # push back preorder block number to the original preorder name_block_number = old_name_rec['block_number'] # check name payment payment_res = check_payment(state_engine, "NAME_REGISTRATION", nameop, fee_block_id, token_address, burn_address, name_fee, block_id) if not payment_res['status']: log.warning( "Name '{}' did not receive the appropriate payment".format(name)) return False log.debug('payment res: {}'.format(payment_res)) # extract payment info token_fee = payment_res['tokens_paid'] token_units = payment_res['token_units'] if token_units == 'BTC': # name was paid for in the preorder by burning BTC, not by spending Stacks # if we paid tokens *as well*, then figure out how many and record it assert token_fee == name_fee, 'Tokens paid in BTC does not match tokens paid in transaction ({} != {})'.format( token_fee, name_fee) # sanity check res = get_stacks_payment(state_engine, nameop, "NAME_REGISTRATION") assert not res['status'], "BUG: we paid in BTC but also Stacks" token_fee = 0 else: if EPOCH_FEATURE_NAMEOPS_COST_TOKENS not in epoch_features: # can't do this---tokens aren't active yet log.warning( 'Tried to pay for {} in Stacks before Stacks exist'.format( name)) return False # name was paid for in the preorder by burning Stacks assert token_fee is not None assert token_units == TOKEN_TYPE_STACKS # sanity check res = get_stacks_payment(state_engine, nameop, "NAME_REGISTRATION") assert res['status'], "BUG: we paid in Stacks but did not" assert res['tokens_paid'] == token_fee assert res['token_units'] == token_units nameop['opcode'] = opcode nameop['op_fee'] = name_fee nameop['token_fee'] = '{}'.format( token_fee) # NOTE: use a string to prevent integer overflow nameop['preorder_hash'] = preorder_hash nameop['importer'] = None nameop['importer_address'] = None nameop['consensus_hash'] = consensus_hash nameop['revoked'] = False nameop['namespace_block_number'] = namespace['block_number'] nameop['first_registered'] = first_registered nameop['last_renewed'] = block_id nameop['preorder_block_number'] = preorder_block_number nameop['block_number'] = name_block_number # not consensus-bearing, but required for SNV nameop['last_creation_op'] = NAME_PREORDER # propagate new sender information nameop['sender'] = nameop['recipient'] nameop['address'] = nameop['recipient_address'] del nameop['recipient'] del nameop['recipient_address'] value_hash = nameop['value_hash'] if value_hash is not None: # deny value hash if we're not in an epoch that supports register/update in one nameop if EPOCH_FEATURE_OP_REGISTER_UPDATE not in epoch_features: log.warning( "Name '{}' has a zone file hash, but this is not supported in this epoch" .format(nameop['name'])) return False log.debug("Adding value hash {} for name '{}'".format( value_hash, nameop['name'])) nameop['value_hash'] = value_hash return True
def check_register( state_engine, nameop, block_id, checked_ops ): """ Verify the validity of a registration nameop. * the name must be well-formed * the namespace must be ready * the name does not collide * either the name was preordered by the same sender, or the name exists and is owned by this sender (the name cannot be registered and owned by someone else) * the mining fee must be high enough. * if the name was expired, then merge the preorder information from the expired preorder (since this is a state-creating operation, we set the __preorder__ and __prior_history__ fields to preserve this). NAME_REGISTRATION is not allowed during a namespace import, so the namespace must be ready. Return True if accepted. Return False if not. """ from ..nameset import BlockstackDB name = nameop['name'] sender = nameop['sender'] # address mixed into the preorder register_addr = nameop.get('recipient_address', None) if register_addr is None: log.debug("No registration address given") return False recipient = nameop.get('recipient', None) if recipient is None: log.debug("No recipient script given") return False name_fee = None namespace = None preorder_hash = None preorder_block_number = None name_block_number = None consensus_hash = None transfer_send_block_id = None fee_block_id = None # block ID at which the fee was paid opcode = nameop['opcode'] first_registered = nameop['first_registered'] # name must be well-formed if not is_b40( name ) or "+" in name or name.count(".") > 1: log.debug("Malformed name '%s': non-base-38 characters" % name) return False # name must not be revoked if state_engine.is_name_revoked( name ): log.debug("Name '%s' is revoked" % name) return False namespace_id = get_namespace_from_name( name ) # namespace must exist and be ready if not state_engine.is_namespace_ready( namespace_id ): log.debug("Namespace '%s' is not ready" % namespace_id) return False # get namespace... namespace = state_engine.get_namespace( namespace_id ) # cannot exceed quota num_names = get_num_names_owned( state_engine, checked_ops, recipient ) if num_names >= MAX_NAMES_PER_SENDER: log.debug("Recipient '%s' has exceeded quota" % recipient) return False # if multisig is not enabled in this epoch, and the recipient # address is a p2sh address, then reject the transaction. # this if for compatibility with 0.13 if virtualchain.is_multisig_address( register_addr ) and not epoch_has_multisig( block_id ): log.debug("Multisig registration address %s, but this epoch (%s) does not support multisig" % (register_addr, get_epoch_number(block_id))) return False # get preorder... preorder = state_engine.get_name_preorder( name, sender, register_addr ) old_name_rec = state_engine.get_name( name, include_expired=True ) if preorder is not None: # Case 1(a-b): registering or re-registering from a preorder # can't be registered already if state_engine.is_name_registered( name ): log.debug("Name '%s' is already registered" % name) return False # name can't be registered if it was reordered before its namespace was ready if not namespace.has_key('ready_block') or preorder['block_number'] < namespace['ready_block']: log.debug("Name '%s' preordered before namespace '%s' was ready" % (name, namespace_id)) return False # name must be preordered by the same sender if preorder['sender'] != sender: log.debug("Name '%s' was not preordered by %s" % (name, sender)) return False # fee was included in the preorder if not 'op_fee' in preorder: log.debug("Name '%s' preorder did not pay the fee" % (name)) return False name_fee = preorder['op_fee'] preorder_hash = preorder['preorder_hash'] preorder_block_number = preorder['block_number'] fee_block_id = preorder_block_number # pass along the preorder state_create_put_preorder( nameop, preorder ) if old_name_rec is None: # Case 1(a): registered for the first time ever log.debug("Registering name '%s'" % name) name_block_number = preorder['block_number'] state_create_put_prior_history( nameop, None ) else: # Case 1(b): name expired, and is now re-registered log.debug("Re-registering name '%s'" % name ) # push back preorder block number to the original preorder name_block_number = old_name_rec['block_number'] transfer_send_block_id = old_name_rec['transfer_send_block_id'] # re-registering prior_hist = prior_history_create( nameop, old_name_rec, preorder_block_number, state_engine, extra_backup_fields=['consensus_hash','preorder_hash','transfer_send_block_id','op_fee','last_creation_op']) state_create_put_prior_history( nameop, prior_hist ) elif state_engine.is_name_registered( name ): # Case 2: we're renewing # name must be owned by the recipient already if not state_engine.is_name_owner( name, recipient ): log.debug("Renew: Name '%s' is registered but not owned by recipient %s" % (name, recipient)) return False # name must be owned by the sender if not state_engine.is_name_owner( name, sender ): log.debug("Renew: Name '%s' is registered but not owned by sender %s" % (name, sender)) return False # fee borne by the renewal if not 'op_fee' in nameop: log.debug("Renew: Name '%s' is registered but renewal did not pay the fee" % (name)) return False log.debug("Renewing name '%s'" % name ) prev_name_rec = state_engine.get_name( name ) first_registered = prev_name_rec['first_registered'] preorder_block_number = prev_name_rec['preorder_block_number'] name_block_number = prev_name_rec['block_number'] name_fee = nameop['op_fee'] preorder_hash = prev_name_rec['preorder_hash'] transfer_send_block_id = prev_name_rec['transfer_send_block_id'] fee_block_id = block_id opcode = "NAME_RENEWAL" # will cause this operation to be re-checked under check_renewal() # pass along prior history prior_hist = prior_history_create( nameop, old_name_rec, block_id, state_engine, extra_backup_fields=['consensus_hash','preorder_hash','transfer_send_block_id','op_fee','last_creation_op']) state_create_put_prior_history( nameop, prior_hist ) state_create_put_preorder( nameop, None ) else: # Case 3: has never existed, and not preordered log.debug("Name '%s' does not exist, or is not preordered by %s" % (name, sender)) return False # check name fee name_without_namespace = get_name_from_fq_name( name ) # fee must be high enough (either the preorder paid the right fee at the preorder block height, # or the renewal paid the right fee at the renewal height) if name_fee < price_name( name_without_namespace, namespace, fee_block_id ): log.debug("Name '%s' costs %s, but paid %s" % (name, price_name( name_without_namespace, namespace, block_id ), name_fee )) return False nameop['opcode'] = opcode nameop['op_fee'] = name_fee nameop['preorder_hash'] = preorder_hash nameop['importer'] = None nameop['importer_address'] = None nameop['consensus_hash'] = consensus_hash nameop['revoked'] = False nameop['namespace_block_number'] = namespace['block_number'] nameop['first_registered'] = first_registered nameop['last_renewed'] = block_id nameop['preorder_block_number'] = preorder_block_number nameop['block_number'] = name_block_number # not consensus-bearing, but required for SNV nameop['transfer_send_block_id'] = transfer_send_block_id nameop['last_creation_op'] = NAME_PREORDER # propagate new sender information nameop['sender'] = nameop['recipient'] nameop['address'] = nameop['recipient_address'] del nameop['recipient'] del nameop['recipient_address'] # regster/renewal return True
def check_register( state_engine, nameop, block_id, checked_ops ): """ Verify the validity of a registration nameop. * the name must be well-formed * the namespace must be ready * the name does not collide * either the name was preordered by the same sender, or the name exists and is owned by this sender (the name cannot be registered and owned by someone else) * the mining fee must be high enough. * if the name was expired, then merge the preorder information from the expired preorder (since this is a state-creating operation, we set the __preorder__ /* and __prior_history__ fields *./ to preserve this). NAME_REGISTRATION is not allowed during a namespace import, so the namespace must be ready. Return True if accepted. Return False if not. """ from ..nameset import BlockstackDB name = nameop['name'] sender = nameop['sender'] # address mixed into the preorder register_addr = nameop.get('recipient_address', None) if register_addr is None: log.debug("No registration address given") return False recipient = nameop.get('recipient', None) if recipient is None: log.debug("No recipient script given") return False if not is_name_valid(name): log.debug('Invalid name') return False epoch_features = get_epoch_features(block_id) name_fee = None namespace = None preorder_hash = None preorder_block_number = None name_block_number = None consensus_hash = None fee_block_id = None # block ID at which the fee was paid burn_address = None # preorder/renew burn address opcode = nameop['opcode'] first_registered = nameop['first_registered'] # name must be well-formed if not is_b40( name ) or "+" in name or name.count(".") > 1: log.debug("Malformed name '%s': non-base-38 characters" % name) return False # name must not be revoked if state_engine.is_name_revoked( name ): log.debug("Name '%s' is revoked" % name) return False namespace_id = get_namespace_from_name( name ) # namespace must exist and be ready if not state_engine.is_namespace_ready( namespace_id ): log.debug("Namespace '%s' is not ready" % namespace_id) return False # get namespace... namespace = state_engine.get_namespace( namespace_id ) # cannot exceed quota num_names = get_num_names_owned( state_engine, checked_ops, recipient ) if num_names >= MAX_NAMES_PER_SENDER: log.debug("Recipient '%s' has exceeded quota" % recipient) return False # if multisig is not enabled in this epoch, and the recipient # address is a p2sh address, then reject the transaction. # this if for compatibility with 0.13 if virtualchain.is_multisig_address( register_addr ) and not epoch_has_multisig( block_id ): log.debug("Multisig registration address %s, but this epoch (%s) does not support multisig" % (register_addr, get_epoch_number(block_id))) return False # get preorder... preorder = state_engine.get_name_preorder( name, sender, register_addr ) old_name_rec = state_engine.get_name( name, include_expired=True ) # TODO: verify that the preorder is fresh. if preorder is not None: # Case 1(a-b): registering or re-registering from a preorder # can't be registered already if state_engine.is_name_registered( name ): log.debug("Name '%s' is already registered" % name) return False # name can't be registered if it was reordered before its namespace was ready if not namespace.has_key('ready_block') or preorder['block_number'] < namespace['ready_block']: log.debug("Name '%s' preordered before namespace '%s' was ready" % (name, namespace_id)) return False # name must be preordered by the same sender if preorder['sender'] != sender: log.debug("Name '%s' was not preordered by %s" % (name, sender)) return False # fee was included in the preorder if not 'op_fee' in preorder: log.debug("Name '%s' preorder did not pay the fee" % (name)) return False name_fee = preorder['op_fee'] preorder_hash = preorder['preorder_hash'] preorder_block_number = preorder['block_number'] fee_block_id = preorder_block_number burn_address = preorder['burn_address'] # pass along the preorder state_create_put_preorder( nameop, preorder ) if old_name_rec is None: # Case 1(a): registered for the first time ever log.debug("Registering name '%s'" % name) name_block_number = preorder['block_number'] else: # Case 1(b): name expired, and is now re-registered log.debug("Re-registering name '%s'" % name ) # push back preorder block number to the original preorder name_block_number = old_name_rec['block_number'] elif state_engine.is_name_registered( name ): # Case 2: we're renewing assert 'burn_address' in nameop, 'BUG: no burn address set in nameop' # pre F-day 2017: name must be owned by the recipient already # post F-day 2017: recipient can be anybody if EPOCH_FEATURE_OP_RENEW_TRANSFER_UPDATE not in epoch_features: # pre F-day 2017 if not state_engine.is_name_owner( name, recipient ): log.debug("Renew: Name '%s' is registered but not owned by recipient %s" % (name, recipient)) return False # name must be owned by the sender if not state_engine.is_name_owner( name, sender ): log.debug("Renew: Name '%s' is registered but not owned by sender %s" % (name, sender)) return False # fee borne by the renewal if not 'op_fee' in nameop or nameop['op_fee'] is None: log.debug("Renew: Name '%s' is registered but renewal did not pay the fee" % (name)) return False log.debug("Renewing name '%s'" % name ) if not state_engine.is_name_owner( name, recipient ): log.debug("Transferring name '{}' to {}".format(name, recipient)) prev_name_rec = state_engine.get_name( name ) first_registered = prev_name_rec['first_registered'] preorder_block_number = prev_name_rec['preorder_block_number'] name_block_number = prev_name_rec['block_number'] name_fee = nameop['op_fee'] preorder_hash = prev_name_rec['preorder_hash'] # transfer_send_block_id = prev_name_rec['transfer_send_block_id'] fee_block_id = block_id burn_address = nameop['burn_address'] opcode = "NAME_RENEWAL" # will cause this operation to be re-checked under check_renewal() state_create_put_preorder( nameop, None ) else: # Case 3: has never existed, and not preordered log.debug("Name '%s' does not exist, or is not preordered by %s" % (name, sender)) return False assert name_fee is not None # check name fee name_without_namespace = get_name_from_fq_name( name ) # fee must be high enough (either the preorder paid the right fee at the preorder block height, # or the renewal paid the right fee at the renewal height) if name_fee < price_name( name_without_namespace, namespace, fee_block_id ): log.debug("Name '%s' costs %s, but paid %s" % (name, price_name( name_without_namespace, namespace, block_id ), name_fee )) return False # fee must be paid to the right address, at the right time. # pre F-day 2017: this *must* be the burn address, and the namespace *must* be version 1 # post F-day 2017: this *may* be the namespace creator's address if not check_burn_address(namespace, burn_address, fee_block_id): log.debug("Invalid burn address {}".format(burn_address)) return False nameop['opcode'] = opcode nameop['op_fee'] = name_fee nameop['preorder_hash'] = preorder_hash nameop['importer'] = None nameop['importer_address'] = None nameop['consensus_hash'] = consensus_hash nameop['revoked'] = False nameop['namespace_block_number'] = namespace['block_number'] nameop['first_registered'] = first_registered nameop['last_renewed'] = block_id nameop['preorder_block_number'] = preorder_block_number nameop['block_number'] = name_block_number # not consensus-bearing, but required for SNV # nameop['transfer_send_block_id'] = transfer_send_block_id nameop['last_creation_op'] = NAME_PREORDER # propagate new sender information nameop['sender'] = nameop['recipient'] nameop['address'] = nameop['recipient_address'] del nameop['recipient'] del nameop['recipient_address'] value_hash = nameop['value_hash'] if value_hash is not None: # deny value hash if we're not in an epoch that supports register/update in one nameop if opcode == 'NAME_REGISTRATION' and EPOCH_FEATURE_OP_REGISTER_UPDATE not in epoch_features: log.debug("Name '{}' has a zone file hash, but this is not supported in this epoch".format(nameop['name'])) return False log.debug("Adding value hash {} for name '{}'".format(value_hash, nameop['name'])) nameop['value_hash'] = value_hash if opcode == 'NAME_REGISTRATION' and 'burn_address' in nameop: # not used in NAME_REGISTRATION (but is used in NAME_RENEWAL) del nameop['burn_address'] # regster/renewal return True