fix(api): publish the AT address as atAddress everywhere - #186
Merged
Conversation
One value, three published names. A DEPLOY_AT transaction carried `aTAddress`,
an AT lookup carried `ATAddress`, and AT map reads plus every other Core
response already used `atAddress`. Both classes bind with
XmlAccessType.FIELD, so the JSON key is literally the Java field name and
neither odd spelling was deliberate — `DeployAtTransactionData` even declared
`getAtAddress()`/`setAtAddress(String AtAddress)` around a field named
`aTAddress`, a fourth capitalization of the same word.
This fails silently, which is why it survived from Qortal: a consumer reading
`atAddress` off a DEPLOY_AT response gets undefined rather than an error. It
cost us a real deployment — the SMPL faucet bootstrap reported "accepted
DEPLOY_AT response did not include an AT address" and aborted after the asset
and the AT had both been issued and confirmed.
Rename the fields on DeployAtTransactionData and ATData to `atAddress`; the
word-case Java accessors (getATAddress) are unchanged. ATStateData's private
field is renamed for consistency though it is not serialized. No consensus
surface: the AT address is derived and never enters the DEPLOY_AT byte layout,
and the transformers use accessors, so transaction bytes and the chain-config
hash are untouched.
Tests assert the marshalled JSON keys directly for both classes and that the
legacy keys are gone, so a future field rename fails loudly instead of quietly
changing the public API.
Also fix two faults found while tracing this. GET /at/{ataddress} validated
nothing, so any unmatched path under /at/ arrived as an "AT address" and an
unknown address answered 204 — making an invented sub-path indistinguishable
from a genuine "no such AT". `/at/search` is not an endpoint, yet it answered
204 and was recorded four times as evidence that no ATs existed. Malformed
addresses are now rejected with INVALID_ADDRESS, while a well-formed address
with no AT still answers empty. GET /at/{ataddress}/data dereferenced a null AT
state for an unknown AT and returned a bare HTTP 500; it now answers empty and
validates the address too. getMapValue directly below already did both, so this
matches the existing in-file contract.
Consumers reading the AT address must use `atAddress`, accepting either name
while nodes on older versions remain in use.
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One value, three published names
DeployAtTransactionData— tx search, unconfirmed, block txs, websocketsaTAddressATData—/at/{addr},/at/byfunctionATAddressatAddressBoth offenders bind with
@XmlAccessorType(XmlAccessType.FIELD), so the JSON key is the Java field name — neither spelling was deliberate.DeployAtTransactionDatadeclaredgetAtAddress()/setAtAddress(String AtAddress)around a field namedaTAddress: a fourth capitalization of the same word in one file.Seven serialized classes already use
atAddress(ATTransactionData,TradeBotData,TradeBotFillData,TransactionSummary,CrossChainOfferSummary,CrossChainTradeSummary,AtMapValueResponse). These two never got the memo — inherited from Qortal.This fails silently, which is why it survived. A consumer reading
atAddressoff a DEPLOY_AT response getsundefined, not an error. It cost a real deployment: the SMPL faucet bootstrap printedaccepted DEPLOY_AT response did not include an AT addressand aborted after the asset and the AT had both been issued and confirmed on Previewnet.Change
Rename the fields on
DeployAtTransactionDataandATDatatoatAddress. The word-case Java accessors (getATAddress()) are unchanged.ATStateData's private field is renamed for consistency though it is not serialized.No consensus surface. The AT address is derived and never enters the DEPLOY_AT byte layout —
DeployAtTransactionTransformerdoesn't reference it at all — and transformers use accessors. Transaction bytes and the chain-config hash are untouched.Tests assert the marshalled JSON keys directly for both classes, and that the legacy keys are gone, so a future field rename fails loudly instead of quietly changing the public API.
Two faults found while tracing this
GET /at/{ataddress}validated nothing. Any unmatched path under/at/arrived as an "AT address", and an unknown address answers 204 — so an invented sub-path was indistinguishable from a genuine "no such AT"./at/searchis not an endpoint, yet it answered 204 and was recorded four times in our own deployment runbook as evidence that no ATs existed. It returns 204 with the faucet live, too. Malformed addresses are now rejected withINVALID_ADDRESS; a well-formed address with no AT still answers empty, as before.GET /at/{ataddress}/datareturned a bare HTTP 500 for an unknown AT — it dereferenced a null AT state. It now answers empty and validates the address.getMapValue, directly below both, already did address validation and null-safe absence, so this matches the existing in-file contract rather than inventing one.Verification
AtResourceTests(8),TransactionDataJsonTests(4),ApiSerializationContractTests,TransactionDataDiscriminatorTests,SmplFaucetAtTests(11),ATMapActivationTests,DeployAtNoNativeAssetTests,DeployAtUnsigned256GateTests. Run with-DskipTests=falseand counts read from the surefire reports, not from a silent exit 0.ATData's field toATAddressfails with the marshalled JSON in the message, empirically confirming the field name drives the key. Removing thegetByAddressvalidation fails withApiException expected: INVALID_ADDRESS. Sources restored byte-identically afterwards (diff -q).qortium-exploreis clean, and every remainingATAddresshit is a Java method name. Core's ownq-apps.jsalready usesatAddressas theGET_ATrequest param, so apps were already sendingatAddressand receivingATAddress.mvn -B test -DskipJUnitTests=false, unfiltered).Follow-ups, not in this PR
atAddresswith anaTAddressfallback — not deprecation politeness, but because the seeds and installed nodes run 1.6.1 for a while yet./at/search(filters over theATstable: codeHash, creator, assetId, executable/finished/frozen/fatal-error, creation range) is worth adding separately; notename/description/aTType/tagslive on the DEPLOY_AT transaction, not on the AT, so they can't be filtered there.