fix(tfchain-client-js): fix stale runtime assumptions in read wrappers (twin ip, entity storage, null crashes)#1101
Merged
Conversation
…ield The Twin struct no longer has an `ip` field (migrated to `relay`/`pk` long ago), but getTwin/listTwins still did `res.ip = hex2a(res.ip)`. hex2a(undefined) dereferences `undefined.length`, so every getTwinByID/listTwins call threw `Cannot read properties of undefined (reading 'length')` on the current runtime. - Remove the stale `ip` decode from getTwin and listTwins; return the metadata-decoded object as-is. - Harden hex2a to return '' for undefined/null. This is the root of the same crash class: hex2a is called on ~16 fields across twin/entity/farms/node/ tfkvstore, several of which are Option<...> (null) on the current runtime (e.g. node serialNumber, publicConfig ipv4/ipv6/gw4/gw6). Null-safe hex2a immunizes all of them. - Add unit tests for hex2a (built-in node:test, no new deps) and wire up the `test` script. Closes #1090 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uard reads Live devnet testing (spec 157) surfaced more stale-runtime-assumption bugs in the read wrappers, beyond the twin `ip` field: - getEntityIDByName queried `entitiesByNameID` and getEntityIDByPubkey queried `entitiesByPubkeyID` — neither exists on the current runtime (renamed to `entityIdByName` / `entityIdByAccountID`). Both threw "is not a function". These are called by activation-service's createEntity, so createEntity was broken against the current runtime. Also normalize not-found to 0 (the new storage decodes to null) to preserve the "0 means absent" contract callers rely on. - getEntity / getNode / getTwin did `res.id` on a null result when the id does not exist, throwing "Cannot read properties of null". Guard with `!res` so they throw a clean "No such <x>" instead. All verified live on devnet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g policy name
More stale-structure bugs found while live-verifying getNode/getPricingPolicyById
on devnet (all returned un-decoded hex):
- getNode public config: read the field as `publicConfig` (camelCase, was the
non-existent `public_config`) and decode its actual nested shape
{ ip4: { ip, gw }, ip6: { ip, gw }, domain } (was a flat { ipv4, ipv6, gw4, gw6 }
that matched nothing).
- getNode location: city/country are nested under `location` on the current
runtime (not top-level), so they were never decoded. Decode city/country/
latitude/longitude inside `location`; drop the dead top-level country/city reads.
- getPricingPolicyById: decode the byte-encoded `name` (was returned as raw hex).
Verified live on devnet: node 50 now returns city "Bartlesville", country
"United States", public IPs/domain readable; policy 1 name "threefold_default_pricing_policy".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #1090, #1102. Expanded from the original twin-
ipfix after live devnet testing surfaced a cluster of bugs of the same class — stale runtime assumptions in the barely-maintained read wrappers. Every fix below is verified live against devnet (spec 157).Bugs fixed
1. twin
ipcrash (#1090) —getTwin/listTwinsdecoded a non-existentipfield;hex2a(undefined)threw. Removed the stale decode and hardenedhex2ato return''onundefined/null(the root of the crash class —hex2ais called on ~16 fields, several nowOption/null).2. Entity lookups queried dead storage (critical — broke activation-service)
getEntityIDByName→entitiesByNameID(gone) →entityIdByNamegetEntityIDByPubkey→entitiesByPubkeyID(gone) →entityIdByAccountIDis not a function. activation-service'screateEntitycalls both, so it was broken against the current runtime. Also normalize not-found →0(new storage decodes tonull).3. Null crashes on non-existent ids —
getEntity/getNode/getTwindidres.idon anulldecode. Guarded to throw a cleanNo such <x>.4.
getNodereturned un-decoded data (wrong key + wrong structure)res.public_config(snake, doesn't exist) with a flat{ipv4,ipv6,gw4,gw6}shape → nothing decoded. Fixed topublicConfigwith the real nested shape{ ip4:{ip,gw}, ip6:{ip,gw}, domain }.city/countryare nested underlocation(not top-level) → never decoded. Now decoded insidelocationalongside lat/long.5.
getPricingPolicyByIdreturned the policynameas raw hex → nowhex2a-decoded.Live verification (devnet, spec 157)
Note correcting the activation-service compatibility assessment
Earlier static analysis concluded activation-service was API-compatible with the v2 client because the method names exist. Live testing disproved that for
createEntity: the methods existed but queried dead storage. This PR is what actually makes the client work against the current runtime — directly relevant to the activation-service v2 upgrade (#1099).Tests
test/util.test.jsforhex2a(built-innode:test, no new deps);testscript wired up.api, so they're covered by the devnet runs above rather than unit tests.🤖 Generated with Claude Code