feat(http): add int64_as_string parameter to query endpoints#1
Open
waynercheung wants to merge 1 commit intodevelopfrom
Open
feat(http): add int64_as_string parameter to query endpoints#1waynercheung wants to merge 1 commit intodevelopfrom
waynercheung wants to merge 1 commit intodevelopfrom
Conversation
b2ce599 to
0587166
Compare
…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.
0587166 to
cb77f36
Compare
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.
What does this PR do?
Closes tronprotocol#6568. Adds an optional
int64_as_stringrequest parameter on whitelisted query servlets. When set totrue,int64/uint64protobuf fields in the response are serialized as quoted JSON strings, avoiding precision loss in clients whose native number type cannot safely represent integers above 2^53 - 1 (most notably JavaScript).Design:
JsonFormat.pushInt64AsString(boolean)returns anAutoCloseablecontrolling a scope-boundThreadLocal. Each whitelisted servlet wraps the final response write intry (AutoCloseable ignored = JsonFormat.pushInt64AsString(flag)) { ... }.GetRewardServlet.doPostparses bothaddressandint64_as_stringfrom the same JSON body in a single read.JsonFormat, and endpoints whose response has no int64 fields are left untouched.JsonFormat.mergeis deliberately not modified — input contract stays strict (see "Known limitation" below)./walletsolidity/*routes inherit the feature via Spring bean reuse inSolidityNodeHttpApiService(two solidity-specific servlets —GetTransactionByIdSolidityServletandGetTransactionInfoByIdSolidityServlet— needed explicit edits)./walletpbft/*routes inherit via Java subclassing: the PBFT query servlets covered here extend their corresponding wallet servlet and only wrap it inwalletOnPBFT.futureGet(...), so the superclass'sint64_as_stringhandling applies without further changes.Why are these changes required?
Full rationale and scope discussion in tronprotocol#6568. TL;DR: JS clients lose precision parsing
int64values likebalance,total_supply, and asset/market balances once the value exceeds 2^53 - 1. This PR gives clients an opt-in way to receive those fields as strings.Supported
/wallet/*endpoints (this list enumerates/wallet/*endpoints only; equivalent/walletsolidity/*and/walletpbft/*routes are supported where those routes already exist and reuse the same servlet logic per the mechanisms described above):Explicitly not supported (code unchanged; the flag is silently ignored):
getnodeinfoJSON.toJSONString(nodeInfo)on a POJO, bypassingJsonFormat; thepushInt64AsStringhelper does not apply.NodeInfohas long fields that do fall within this issue's domain; supporting them needs a separate fastjson-based mechanism. Deferred to follow-up as a deliberate scope cut, not a claim of "no precision risk".getsignweight,getapprovedlistTransactionExtentionthat clients round-trip back for signing.getBrokerageint, notlong.getbandwidthprices,getenergyprices,getmemofeePricesResponseMessage.prices) has no int64 fields.gettransactionlistfrompendingTransactionIdListhas no int64 fields (repeated string txIdonly).getmarketpairlistMarketOrderPairhas onlybytestoken ids.listnodesNodeList/Node/Addresscontain onlystring ipandint32 port.create*/trigger*/broadcast*/deploy*/transfer*/freeze*/unfreeze*/vote*/proposal*/exchange*(create/inject/transaction/withdraw) / write-path market endpoints (e.g.marketsellasset,marketcancelorder) /participate*/withdraw*JsonFormat.mergeon the client side for signing and re-broadcasting.This PR has been tested by:
JsonFormatInt64AsStringTest(20 cases): default behavior, int64/uint64 quoting, non-int64 fields unaffected, nested / map / boundary values (2^53 ± 1,Long.MAX/MIN,-1), scope lifecycle (clean close, exception, explicit close, nested scopes), thread isolation, thread-reuse anti-pollution, PostParams body parsing.UtilMockTest(+3 cases):Util.decodeAddressblank-input and mainnet-hex branches,Util.processAddressErrorerror-JSON shape.GetRewardServletTest— hand-rolled JSON servlet + POST JSON body fix.GetNowBlockServletTest— JsonFormat path withdoPost → doGetdelegation.GetBlockServletTest(new) — shared privatehandle()with customparseParams.GetPaginatedProposalListServletTest(new) —doPostthat manually reads the body viaUtil.getInt64AsStringPost.ListExchangesServletTest(new) — URL-query-only POST contract.testPostBodyFlagIsIgnoredpins the counter-intuitive "body-only flag must not take effect" semantic against future refactors.org.tron.core.services.http.*continue to pass../gradlew :framework:checkstyleMain :framework:checkstyleTestpasses.Follow up
developers.tron.networkAPI reference needs anint64_as_stringparameter entry for each supported endpoint plus a top-level section documenting GET vs POST read paths and the round-trip limitation.GetNodeInfoServlet: addint64_as_stringsupport via a fastjson localSerializeConfigbindingLong/longtoToStringSerializer, since this endpoint bypassesJsonFormat. Deferred as a deliberate scope cut.Extra details
Transaction/BlockresponsesJsonFormat.merge()only accepts unquoted 64-bit integer tokens. As a result, responses from block and transaction endpoints whose payload embeds aTransactionorBlockprotobuf (thegetnowblock/getblock*family andgettransactionbyid/gettransactionfrompending, plus their/walletsolidity/*and/walletpbft/*counterparts where exposed) cannot be locally parsed back into a protobuf message viaJsonFormat.mergewhenint64_as_string=trueis set. This is a deliberate design choice, not a bug — keepingmergestrict preserves backward-compatible input semantics and avoids widening the signing/broadcast input contract. Clients needing round-trip simply don't set the flag.Thread safety & wire compatibility
Preserve-restore
ThreadLocalwith try-with-resources guarantees cleanup on normal return, exception, and nested scopes; explicitly validated against Jetty'sQueuedThreadPoolreuse bynoPollutionOnThreadReuseandthreadIsolationunit tests.Zero protobuf schema changes. Default behavior (flag absent) is preserved: existing clients see no change in response encoding.
GetRewardServlet.doPostwas restructured soapplication/jsonPOST parses bothaddressandint64_as_stringfrom a single body read — fixes a body-internal inconsistency surfaced during review (address from body, flag from URL query). Form-encoded POSTs still delegate todoGet.