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)
- Ensure node is synced (I initially got
BLOCKCHAIN_NEEDS_SYNC; after syncing, issue persisted).
- Build/sign a
REGISTER_NAME tx for Qombo using /names/register.
- Submit via
/transactions/process.
- Query:
GET /names/Qombo
GET /names/qombo
- 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
- 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).
- Prevent side-effecting integrity repair from transaction/block validation critical paths, or make it fully deterministic and consensus-safe.
- Ensure block validation behavior is consistent across all sync paths regarding preprocessing assumptions.
- 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.
Summary
I hit an inconsistency where a
REGISTER_NAMEtransaction is rejected asNAME_ALREADY_REGISTERED, while the same name resolves as unknown via the names API.This appears related to
REGISTER_NAMEpre-processing mutating theNamestable (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
~/github/qortal/admin/info:qortal-6.0.0-e67352ce67352ce...Reproduction (API-level)
BLOCKCHAIN_NEEDS_SYNC; after syncing, issue persisted).REGISTER_NAMEtx forQombousing/names/register./transactions/process.GET /names/QomboGET /names/qomboGET /transactions/search?txType=REGISTER_NAME&confirmationStatus=UNCONFIRMED&limit=0Actual behavior
/transactions/processreturnsTRANSACTION_INVALIDwithNAME_ALREADY_REGISTERED./names/QomboreturnsNAME_UNKNOWN./names/qomboreturnsNAME_UNKNOWN.REGISTER_NAMEtx exists for that name.Expected behavior
/names/{sanitizedName}is unknown and there is no confirmed current owner for that reduced name,REGISTER_NAMEshould be processable.NAME_ALREADY_REGISTERED.Technical analysis (suspected root cause)
1)
REGISTER_NAMEpreProcess mutates name state before processability checksTransaction.importAsUnconfirmed()callspreProcess()beforeisValidUnconfirmed():src/main/java/org/qortal/transaction/Transaction.java:840REGISTER_NAME.preProcess()calls:NamesDatabaseIntegrityCheck.rebuildName(...)src/main/java/org/qortal/transaction/RegisterNameTransaction.java:117src/main/java/org/qortal/transaction/RegisterNameTransaction.java:124isProcessable()then checksreducedNameExists(...):src/main/java/org/qortal/transaction/RegisterNameTransaction.java:103src/main/java/org/qortal/transaction/RegisterNameTransaction.java:105So
preProcess()can influence whetherisProcessable()returnsNAME_ALREADY_REGISTERED.2) Fallback name-history lookup for
UPDATE_NAMEappears case-sensitive/incompleteIn fallback query path:
src/main/java/org/qortal/controller/repository/NamesDatabaseIntegrityCheck.java:435src/main/java/org/qortal/controller/repository/NamesDatabaseIntegrityCheck.java:437src/main/java/org/qortal/controller/repository/NamesDatabaseIntegrityCheck.java:438Current criteria:
(name = ? OR new_name = ? OR (reduced_new_name != '' AND reduced_new_name = ?))(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:165src/main/java/org/qortal/api/resource/NamesResource.java:171reducedNameExists(...)after preProcess mutation:src/main/java/org/qortal/transaction/RegisterNameTransaction.java:105Result: user sees name as "available/unknown", but tx process returns already registered.
Potential network/consensus risk
There is a concerning path inconsistency around
preProcess():newBlock.preProcess()beforeisValid():src/main/java/org/qortal/controller/Synchronizer.java:1514preProcess()call:src/main/java/org/qortal/controller/Synchronizer.java:1640src/main/java/org/qortal/controller/Synchronizer.java:1741Given
REGISTER_NAME.preProcess()mutates repository state, this could lead to path-dependent block acceptance/rejection behavior in edge cases.Suggested fix directions
OldName/oldnamestyle chains),NAME_UNKNOWN+NAME_ALREADY_REGISTEREDmismatch,Why this matters
This currently presents as: