Skip to content

REGISTER_NAME can return NAME_ALREADY_REGISTERED while /names/{name} is NAME_UNKNOWN (preProcess name rebuild side-effects, case-variant history) #314

Description

@QuickMythril

Summary

I hit an inconsistency where a REGISTER_NAME transaction is rejected as NAME_ALREADY_REGISTERED, while the same name resolves as unknown via the names API.

This appears related to REGISTER_NAME pre-processing mutating the Names table (rebuildName(...)) before processability checks, combined with case-sensitive history lookup in name-integrity fallback queries.

I also see a potential mixed-version/network consistency risk because some sync paths run preProcess() before block validation and others do not.

Environment

  • Observed date: 2026-02-23
  • Core repo: ~/github/qortal
  • Running build reported by /admin/info: qortal-6.0.0-e67352c
  • Local repo HEAD at time of check: e67352ce...

Reproduction (API-level)

  1. Ensure node is synced (I initially got BLOCKCHAIN_NEEDS_SYNC; after syncing, issue persisted).
  2. Build/sign a REGISTER_NAME tx for Qombo using /names/register.
  3. Submit via /transactions/process.
  4. Query:
    • GET /names/Qombo
    • GET /names/qombo
  5. Optionally verify unconfirmed register txs:
    • GET /transactions/search?txType=REGISTER_NAME&confirmationStatus=UNCONFIRMED&limit=0

Actual behavior

  • /transactions/process returns TRANSACTION_INVALID with NAME_ALREADY_REGISTERED.
  • /names/Qombo returns NAME_UNKNOWN.
  • /names/qombo returns NAME_UNKNOWN.
  • No unconfirmed REGISTER_NAME tx exists for that name.

Expected behavior

  • If /names/{sanitizedName} is unknown and there is no confirmed current owner for that reduced name, REGISTER_NAME should be processable.
  • Name integrity repair should not create state that causes false NAME_ALREADY_REGISTERED.
  • Validation behavior should be consistent across sync/validation paths.

Technical analysis (suspected root cause)

1) REGISTER_NAME preProcess mutates name state before processability checks

  • Transaction.importAsUnconfirmed() calls preProcess() before isValidUnconfirmed():
    • src/main/java/org/qortal/transaction/Transaction.java:840
  • REGISTER_NAME.preProcess() calls:
    • NamesDatabaseIntegrityCheck.rebuildName(...)
    • src/main/java/org/qortal/transaction/RegisterNameTransaction.java:117
    • src/main/java/org/qortal/transaction/RegisterNameTransaction.java:124
  • isProcessable() then checks reducedNameExists(...):
    • src/main/java/org/qortal/transaction/RegisterNameTransaction.java:103
    • src/main/java/org/qortal/transaction/RegisterNameTransaction.java:105

So preProcess() can influence whether isProcessable() returns NAME_ALREADY_REGISTERED.

2) Fallback name-history lookup for UPDATE_NAME appears case-sensitive/incomplete

In fallback query path:

  • src/main/java/org/qortal/controller/repository/NamesDatabaseIntegrityCheck.java:435
  • src/main/java/org/qortal/controller/repository/NamesDatabaseIntegrityCheck.java:437
  • src/main/java/org/qortal/controller/repository/NamesDatabaseIntegrityCheck.java:438

Current criteria:

  • (name = ? OR new_name = ? OR (reduced_new_name != '' AND reduced_new_name = ?))
  • args: (name, name, reducedName)

This can miss case-variant rename chains (especially on old-name side), allowing partial/inconsistent rebuild outcomes.

3) API check path and processability check can diverge

  • /names/{name} resolves by reduced name:
    • src/main/java/org/qortal/api/resource/NamesResource.java:165
    • src/main/java/org/qortal/api/resource/NamesResource.java:171
  • Processability uses reducedNameExists(...) after preProcess mutation:
    • src/main/java/org/qortal/transaction/RegisterNameTransaction.java:105

Result: user sees name as "available/unknown", but tx process returns already registered.

Potential network/consensus risk

There is a concerning path inconsistency around preProcess():

  • Reorg sync path runs newBlock.preProcess() before isValid():
    • src/main/java/org/qortal/controller/Synchronizer.java:1514
  • Other sync paths validate without explicit preProcess() call:
    • src/main/java/org/qortal/controller/Synchronizer.java:1640
    • src/main/java/org/qortal/controller/Synchronizer.java:1741

Given REGISTER_NAME.preProcess() mutates repository state, this could lead to path-dependent block acceptance/rejection behavior in edge cases.

Suggested fix directions

  1. Make name-history matching deterministic on reduced-name semantics for both old/new names in integrity-rebuild queries (avoid raw case-sensitive mismatch for relevant comparisons).
  2. Prevent side-effecting integrity repair from transaction/block validation critical paths, or make it fully deterministic and consensus-safe.
  3. Ensure block validation behavior is consistent across all sync paths regarding preprocessing assumptions.
  4. Add regression tests for:
    • case-variant rename history (OldName/oldname style chains),
    • NAME_UNKNOWN + NAME_ALREADY_REGISTERED mismatch,
    • same block acceptance across sync paths.

Why this matters

This currently presents as:

  • user-facing false "name unavailable" behavior,
  • API inconsistency ("unknown" vs "already registered"),
  • potential mixed-node acceptance divergence in edge cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions