Skip to content

feat(api): support int64_as_string parameter for GET requests #6699

Open
waynercheung wants to merge 1 commit intotronprotocol:developfrom
waynercheung:feat/support_param_int64
Open

feat(api): support int64_as_string parameter for GET requests #6699
waynercheung wants to merge 1 commit intotronprotocol:developfrom
waynercheung:feat/support_param_int64

Conversation

@waynercheung
Copy link
Copy Markdown
Collaborator

@waynercheung waynercheung commented Apr 23, 2026

What does this PR do?

Adds an opt-in int64_as_string query parameter on TRON HTTP GET endpoints. When set, int64 / uint64 protobuf fields in the response are serialized as quoted JSON strings (e.g. "123456789012345" instead of 123456789012345), preventing precision loss in clients whose native number type cannot safely represent integers above 2^53 - 1 (e.g. JavaScript).

# Without flag (unchanged from current develop)
$ curl http://node/wallet/getnowblock | jq .block_header.raw_data.timestamp
1700000000000
 
# With flag
$ curl 'http://node/wallet/getnowblock?int64_as_string=true' | jq .block_header.raw_data.timestamp
"1700000000000"

Scope: GET only, by design.

POST is intentionally unsupported. Reading the body in a centralized location (RateLimiterServlet.service or a Servlet Filter) would consume request.getReader(), which TRON's existing 25 servlets already consume themselves for business logic. Centralizing POST would require introducing a CachingHttpServletRequestWrapper to make the body re-readable -- significant infrastructure (~150 lines), runtime overhead (every POST body byte-copied to memory), and edge cases (multipart, encoding, large bodies). The simpler and lower-risk approach is to limit this flag to GET.

Most TRON query endpoints support both GET and POST. JS clients that need precision can use the GET form. POST-only write endpoints (Transfer / Trigger / Proposal*) return Transaction proto whose int64 fields would break round-trip JsonFormat.merge if quoted -- those clients should not enable this flag in the first place.

Backward compatibility: when the parameter is absent or false, every response is byte-identical to current develop -- same JSON shape, same error messages, same log output, same exception paths.


Why are these changes required?

JavaScript Number is IEEE 754 double-precision with 53 bits of mantissa. Any int64 / uint64 value larger than 2^53 - 1 loses precision when consumed as a JS number -- for example, an account balance of 9007199254740993 becomes 9007199254740992. Many TRON HTTP fields routinely exceed this threshold (balance, frozen.amount, energy/bandwidth limits, transaction fee_limit, block timestamp in microseconds, etc.).

The standard mitigation across protobuf JSON ecosystems (proto3 JSON spec, Google's protobuf-java JsonFormat) is to emit 64-bit integers as quoted strings. This PR brings TRON's HTTP API in line with that convention while keeping the new behavior fully opt-in.

Closes #6568.


This PR has been tested by:

  • Unit Tests -- 19 tests across 2 files, all passing locally:

    • JsonFormatInt64AsStringTest (13 tests): default behavior, int64 / uint64 quoting, non-int64 fields unaffected, nested / map / boundary values (2^53 ± 1, Long.MAX/MIN, -1), state cleanup (normal close, after exception, explicit clear), thread isolation, thread-reuse anti-pollution.
    • RateLimiterServletInt64Test (6 tests): end-to-end integration pinning four contracts:
      1. GET with ?int64_as_string=true produces quoted int64 fields.
      2. GET without flag produces unquoted int64 fields (regression baseline).
      3. POST never honors the flag (pins the GET-only design contract).
      4. service()'s finally block clears the ThreadLocal (defends against silent leak across reused Tomcat threads).
      5. Hand-built JSON servlets (GetBurnTrx / GetPendingSize / GetTransactionCountByBlockNum / GetReward) honor the flag through their isInt64AsString() ? quoted : unquoted ternary -- mutation-tested against ternary inversion.
      6. Hand-built JSON servlets default to unquoted output (regression baseline).
  • Manual Testing -- recommended pre-merge: hit a few endpoints with and without the flag, diff the JSON shape, confirm no regression on the default path.


Follow up

  1. Documentation update -- client-facing docs should note: (1) the flag is honored only on GET requests; (2) when set, responses containing Transaction / Block / TransactionExtention / TransactionSignWeight / TransactionApprovedList proto structures cannot be re-parsed by JsonFormat.merge (the parser does not accept quoted int64 input). Clients that need precision and only consume the response should enable the flag; clients that need round-trip should leave it off.
  2. POST support (optional, future) -- if clients later request POST support, it can be added in an independent follow-up PR by introducing a Servlet Filter + CachingHttpServletRequestWrapper. The trade-offs of that approach (body memory caching, multipart edge cases) deserve their own scoped review.
  3. JSON-RPC -- out of scope for this PR. If JS clients hit similar precision issues via the JSON-RPC layer, it would be addressed separately.

Extra details

Files changed (9 total).

Core machinery:

  • JsonFormat.java: INT64_AS_STRING ThreadLocal + setInt64AsString / clearInt64AsString / isInt64AsString helpers; split INT64 and UINT64 branches in printFieldValue to emit quoted strings only when the flag is set. When the flag is unset, the code path is byte-identical to develop.
  • Util.java: INT64_AS_STRING constant + getInt64AsString (URL query, mirrors getVisible).
  • RateLimiterServlet.service: set ThreadLocal from URL query on GET only; clear in finally so reused Tomcat threads do not leak state across requests. The finally clear has a defensive comment because removing it silently breaks request isolation.
    Hand-built JSON responses (4 files) -- these emit JSON literals manually instead of going through JsonFormat.printToString, so they read JsonFormat.isInt64AsString() to choose between quoted and unquoted format:
  • GetBurnTrxServlet: burnTrxAmount field
  • GetPendingSizeServlet: pendingSize field
  • GetTransactionCountByBlockNumServlet: count field
  • GetRewardServlet: reward field
    Test coverage:
  • JsonFormatInt64AsStringTest (mechanism level)
  • RateLimiterServletInt64Test (end-to-end integration, including critical defense against ThreadLocal leakage)
    ~50 other read-only query servlets automatically pick up the flag through the centralized RateLimiterServlet.service GET path with zero changes at the call site.

Sample contract for clients

int64_as_string parameter (issue #6568):
 
  Format:
    GET /wallet/<endpoint>?int64_as_string=true
 
  Behavior:
    When set, all int64/uint64 fields in the response are serialized as
    quoted JSON strings. This avoids precision loss in clients whose native
    number type cannot safely represent integers above 2^53 - 1 (e.g.,
    JavaScript).
 
  Limitation:
    Only honored on GET requests. POST requests always emit unquoted int64
    fields. If you need precision-safe responses, use GET; the same data is
    available on the GET form of every dual-method endpoint.
 
  Round-trip note:
    Responses containing Transaction / Block / TransactionExtention /
    TransactionSignWeight / TransactionApprovedList proto structures
    cannot be re-parsed by JsonFormat.merge when this flag is enabled.
    If your client needs round-trip (e.g., signing, broadcasting), do not
    enable this flag.

Comment thread framework/src/main/java/org/tron/core/services/http/Util.java Fixed
@waynercheung waynercheung reopened this Apr 23, 2026
@waynercheung waynercheung changed the title feat(http): add int64_as_string parameter to query endpoints feat(http): add int64_as_string parameter to query endpoints Apr 23, 2026
@halibobo1205 halibobo1205 added this to the GreatVoyage-v4.8.2 milestone Apr 23, 2026
@halibobo1205 halibobo1205 added the topic:api rpc/http related issue label Apr 23, 2026
@waynercheung waynercheung force-pushed the feat/support_param_int64 branch 6 times, most recently from b2ce599 to 0587166 Compare May 1, 2026 07:22
@waynercheung waynercheung changed the title feat(http): add int64_as_string parameter to query endpoints feat(api): support int64_as_string parameter for GET requests May 1, 2026
…otocol#6568)

Add an opt-in `int64_as_string` query parameter on TRON HTTP GET endpoints.
When set, int64/uint64 protobuf fields in the response are serialized as
quoted JSON strings to avoid precision loss in clients whose native number
type cannot safely represent integers above 2^53 - 1 (e.g. JavaScript).

Scope: GET only. POST is intentionally unsupported because reading the
request body in a centralized location (RateLimiterServlet.service or a
Filter) would consume request.getReader() and break downstream servlets
that read the body themselves. Most TRON query endpoints support both
GET and POST, so clients that need precision can use the GET form. POST-
only write endpoints return Transaction proto whose int64 fields would
break round-trip JsonFormat.merge if quoted, so they should not enable
this flag in the first place.

- JsonFormat: add INT64_AS_STRING ThreadLocal + setInt64AsString /
  clearInt64AsString / isInt64AsString helpers; split printFieldValue
  INT64/SINT64/SFIXED64 and UINT64/FIXED64 branches so they emit quoted
  strings only when the flag is set.
- Util: add INT64_AS_STRING constant + getInt64AsString (URL query,
  mirrors getVisible).
- RateLimiterServlet.service: set ThreadLocal from URL query on GET only;
  clear in finally so reused Tomcat threads do not leak state across
  requests.
- GetBurnTrx / GetPendingSize / GetTransactionCountByBlockNum: emit
  quoted int64 in their hand-built JSON responses when isInt64AsString
  is true.
- JsonFormatInt64AsStringTest: covers default behavior, int64 / uint64
  quoting, non-int64 fields unaffected, nested / map / boundary values
  (2^53 +/- 1, Long.MAX/MIN, -1), state cleanup (normal close, after
  exception, explicit clear), thread isolation, thread-reuse anti-pollution.

Backward compatibility: requests without int64_as_string=true produce
byte-identical responses to develop -- the new code paths are gated
entirely on the new flag.

Closes tronprotocol#6568.
@waynercheung waynercheung force-pushed the feat/support_param_int64 branch from 0587166 to cb77f36 Compare May 1, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:api rpc/http related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regarding the potential numeric overflow issue when querying asset values

3 participants