You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Some JSON-RPC methods dereference a null parameter without a null check, throw NullPointerException, and answer the client with jsonrpc4j's fallback error. On Java 8 the response is:
This issue defines the null handling of 10 parameter positions and 7 optional DTO fields:
A null required object parameter, filter ID or fullTransactionObjects returns -32602 on endpoints where the method is available.
An optional DTO field explicitly set to null is treated as omitted.
This issue handles the listed null inputs and aligns eth_uninstallFilter lookup-miss results for non-null IDs with go-ethereum. Other behavior of non-null inputs, HTTP status codes, gRPC and HTTP API behavior remain unchanged.
This issue covers how these methods themselves check and answer a null, together with the eth_uninstallFilter lookup-miss result; it does not touch the framework layer. The shape of the fallback response for unmapped exceptions (-32001, echoed exception class name) is a separate problem, tracked in #6941.
Problem
Motivation
message: null violates JSON-RPC 2.0 section 5.1, which defines message as "A String providing a short description of the error" (null is not a String). On JVMs where helpful NullPointerException messages are enabled, which is the default from JDK 15 onwards, message instead carries a diagnostic string naming internal fields and method signatures.
data echoes the Java exception class name, which clients should not depend on.
-32001 is registered in the public error catalog as a server-side internal error, so clients cannot tell that they passed a bad parameter.
The same class of input gets different results across methods (an error, a success, or a different code), so clients cannot handle it uniformly.
Current State
10 parameter positions have execution paths that can dereference a null argument:
7 optional DTO fields behave differently when explicitly null than when omitted: tokenId / tokenValue on BuildArguments (return -32001), consumeUserResourcePercent / originEnergyLimit / permissionId / extraData (wrapped into -32000 by the builder's catch-all), and CallArguments.from (returns -32602, whereas omitting it continues with the zero address).
Audit scope: the null handling of all 52 methods on TronJsonRpc was checked one by one, with the result below. This issue only covers positions where a null can lead to an unhandled exception, or DTO fields whose explicit null differs from omission. These positions are covered as a whole, including the existing successful eth_getBlockByNumber branch for a non-existent block and a null fullTransactionObjects; positions that already reject null are left alone; positions where null already has a definite but debatable result are behavior changes that need their own discussion. Following the discussion below, one lookup-miss behavior is additionally taken into this issue, eth_uninstallFilter returning false instead of an error, avoiding a separate intermediate policy for uninstall lookup misses. The other behavior changes stay out of scope.
Category
Count
Handling
Methods without parameters
16 methods
not applicable
Unimplemented methods whose body only throws -32601, so the parameters never reach the business logic
11 methods
not applicable
hash / address / block number / storage key parameters that already return -32602 on null (most of these null checks were added by #6828)
10 positions
already correct, unchanged
Positions with paths that can throw NullPointerException on null
10 positions
this issue
Optional DTO fields whose explicit null differs from omission
7 fields
this issue
Null has a definite but debatable result: web3_sha3(null) returns the hash of empty input; whether the transaction index is validated depends on whether the block exists; the 4 uncle methods validate nothing; an optional block parameter that is null returns an error (-32600 for eth_call) instead of being treated as latest
several
behavior changes, separate issues
On the baseline verified here, the node logs nothing for these calls, because JsonRpcServlet sets setShouldLogInvocationErrors(false) and the fallback path has no log point of its own. develop @ 4a21592 and GreatVoyage-v4.8.2.1 are both affected; verified on Java 8 and Java 17. Reproduce:
Clients matching the old codes (-32001 / -32000) on these paths will observe a change.
Consensus, chain state and funds are not involved; the current response may carry an exception class name and, on JVMs with helpful NullPointerException messages, JVM-generated diagnostic text; the responses shown here contain no stack trace, path or configuration.
Proposed Solution
Proposed Design
Basis, in priority order: the Execution API required / schema; whether null can reasonably be taken as the zero value; go-ethereum (v1.17.6) / Besu (26.9.0) behavior as a reference.
The null-input results below apply on endpoints where the methods are available. Existing request-source checks retain precedence; unavailable methods keep their current errors.
JSON-RPC 2.0 / Execution API required. go-ethereum v1.17.6 and Besu 26.9.0 also reject it with -32602; go-ethereum v1.17.5 and earlier decoded it as a zero value. The comparison applies to the four eth_ methods; the TRON-specific buildTransaction follows the same required-object policy.
Null filter ID in eth_uninstallFilter / eth_getFilterChanges / eth_getFilterLogs
-32602 "invalid params"
required ID. go-ethereum v1.17.6 (ethereum/go-ethereum#35576) and Besu 26.9.0 reject null here with -32602; go-ethereum v1.17.5 and earlier decoded it as an empty ID
eth_uninstallFilter with a non-null ID that is unknown or already removed
returns false
aligned with go-ethereum, whose UninstallFilter reports whether a filter was found; removing an installed filter still returns true
eth_getFilterChanges / eth_getFilterLogs with a non-null unknown ID
-32000 "filter not found", unchanged
same as go-ethereum and Besu
Null fullTransactionObjects
-32602 "invalid params". The block hash or selector is validated first, and the flag is checked before any block lookup, so the result does not depend on whether the block exists
required boolean. go-ethereum v1.17.6 and Besu 26.9.0 reject null here with -32602; go-ethereum v1.17.5 and earlier decoded it as false
Optional DTO field explicitly null (7 fields)
same as omitting the field
the zero value is the field default; for CallArguments.from, go-ethereum (a pointer field) and Besu behave the same way
Error responses keep the existing annotation mapping: data is "{}" and the id is echoed.
Key Changes
Null-check the 10 positions after the request-source check and before the business logic. For eth_getBlockByHash / eth_getBlockByNumber, the block hash or selector is validated first, then fullTransactionObjects, before any block lookup; a null flag is no longer unboxed.
Add @JsonSetter(nulls = Nulls.SKIP) to the 7 DTO fields; all 7 already declare non-null default initializers (0L / 0 / "" / the zero address), so skipping the setter on an explicit null lands exactly on the omitted-field semantics.
eth_call validates its required transaction argument before the block parameter, so eth_call([null, null]) goes from -32600 to -32602.
The change is limited to the JSON-RPC layer of the framework module: TronJsonRpc, TronJsonRpcImpl, JsonRpcApiUtil, LogFilter, BuildArguments, CallArguments. Remove the obsolete ItemNotFoundException declaration and mapping from eth_uninstallFilter; the other filter methods retain them. Removing a throws clause keeps existing Java binaries compatible, but source callers that specifically catch that checked exception, or implementations that still declare it, may need adjustment when recompiled. On TronJsonRpcImpl, uninstallFilter and getFilterChanges also declare JsonRpcInvalidParamsException, which TronJsonRpc already declares for them.
Impact
Security: removes Java exception types and diagnostic text from these error responses; no impact on consensus or transaction execution has been identified.
Stability: null inputs no longer reach the NPE fallback path.
Performance: null handling adds small local checks; uninstall reuses the existing maps and avoids a separate presence lookup before removal, without introducing application-level locks or full-map scans.
Developer Experience: error codes become interpretable against the specification; clients no longer need to parse Java class names.
Compatibility
Item
Result
Breaking Change
Yes, limited to the affected null-input and filter lookup-miss responses. Successful valid calls remain unchanged.
Default Behavior Change
Yes. Null object parameters, null filter IDs and null fullTransactionObjects-32001 -> -32602; eth_getBlockByNumber with a non-existent block and a null fullTransactionObjects goes from result: null to -32602; eth_uninstallFilter returns false for any non-null ID that does not identify an installed filter, replacing the previous -32000; explicit null DTO fields equal omission; eth_call([null, null]) goes from -32600 to -32602.
Migration Required
Conditional. Clients matching the old codes on these paths need to adjust, as do clients that branch on whether eth_uninstallFilter answers with an error or with a result.
ByteArray.fromHex only strips a 0x prefix and left-pads to an even length, so it normalizes rather than validates. Once a lookup miss returns false, an empty string or a non-hexadecimal string also returns false instead of -32000, because they simply fail the lookup. This issue does not add format validation as a side effect.
Every -32001 above is jsonrpc4j's fallback for an exception without an @JsonRpcErrors mapping. If #6941 lands first, that fallback becomes -32603 "Internal error" without data, so only the observed "before" side of these rows changes; the target behavior defined by this issue is the same either way.
The following remain unchanged: results of valid non-null requests other than the eth_uninstallFilter lookup misses above, HTTP status codes, the request-source check, wildcard semantics of nulls inside a filter object, gRPC and non-JSON-RPC HTTP API behavior.
Acceptance Criteria
Each row's result or error object is verified through a real JsonRpcServer; error assertions cover code, message and data, including the absence of Java exception class names.
DTO fields are verified through ObjectMapper deserialization: an explicit null and an omitted field give the same result.
The request-source check on a SolidityNode / under PBFT is unchanged.
A null filter ID returns -32602 "invalid params" from all three filter-ID methods on supported endpoints; PBFT retains -32601.
A null fullTransactionObjects returns -32602 "invalid params" for existing and non-existent blocks; an invalid block hash or selector keeps its own error, and a null flag is rejected before any block is read.
eth_uninstallFilter returns false for an unknown ID and a second removal of the same ID, and true only when an installed filter is removed.
eth_uninstallFilter returns false for an empty string and for a non-hexadecimal string, and no format validation is introduced.
Wildcard semantics of nulls inside a filter object are unchanged.
Follow-up
Outside the scope of this issue and not blocking its closure:
Missing params, params: null and params: [] retain existing dispatch and arity behavior and are not universally rejected: whether they are accepted depends on how many parameters the method declares. This issue covers explicit null values in the argument positions and DTO fields listed above, plus the eth_uninstallFilter lookup-miss result.
The remaining "behavior changes" row of the audit table: web3_sha3(null), unconditional transaction index validation, parameter validation for the uncle methods, and normalizing an omitted / null optional block parameter to latest; each gets its own issue.
PR fix(jsonrpc): harden RPC/HTTP parameter validation #6828 (merged into develop) added null checks for hash / address / block number / storage key parameters; this issue builds on top of it, has no pending prerequisite, and adds wire regressions for 4 of the hash-parameter methods.
Summary
Some JSON-RPC methods dereference a
nullparameter without a null check, throwNullPointerException, and answer the client with jsonrpc4j's fallback error. On Java 8 the response is:{"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":null,"data":"java.lang.NullPointerException"}}This issue defines the
nullhandling of 10 parameter positions and 7 optional DTO fields:fullTransactionObjectsreturns-32602on endpoints where the method is available.nullis treated as omitted.This issue handles the listed null inputs and aligns
eth_uninstallFilterlookup-miss results for non-null IDs with go-ethereum. Other behavior of non-null inputs, HTTP status codes, gRPC and HTTP API behavior remain unchanged.This issue covers how these methods themselves check and answer a null, together with the
eth_uninstallFilterlookup-miss result; it does not touch the framework layer. The shape of the fallback response for unmapped exceptions (-32001, echoed exception class name) is a separate problem, tracked in #6941.Problem
Motivation
message: nullviolates JSON-RPC 2.0 section 5.1, which definesmessageas "A String providing a short description of the error" (nullis not a String). On JVMs where helpful NullPointerException messages are enabled, which is the default from JDK 15 onwards,messageinstead carries a diagnostic string naming internal fields and method signatures.dataechoes the Java exception class name, which clients should not depend on.-32001is registered in the public error catalog as a server-side internal error, so clients cannot tell that they passed a bad parameter.Current State
10 parameter positions have execution paths that can dereference a null argument:
eth_getLogs,eth_newFilter,eth_estimateGas,eth_call,buildTransactioneth_uninstallFilter,eth_getFilterChanges,eth_getFilterLogsfullTransactionObjects(Booleanauto-unboxing):eth_getBlockByHash,eth_getBlockByNumber7 optional DTO fields behave differently when explicitly null than when omitted:
tokenId/tokenValueonBuildArguments(return-32001),consumeUserResourcePercent/originEnergyLimit/permissionId/extraData(wrapped into-32000by the builder's catch-all), andCallArguments.from(returns-32602, whereas omitting it continues with the zero address).Audit scope: the null handling of all 52 methods on
TronJsonRpcwas checked one by one, with the result below. This issue only covers positions where a null can lead to an unhandled exception, or DTO fields whose explicit null differs from omission. These positions are covered as a whole, including the existing successfuleth_getBlockByNumberbranch for a non-existent block and a nullfullTransactionObjects; positions that already reject null are left alone; positions where null already has a definite but debatable result are behavior changes that need their own discussion. Following the discussion below, one lookup-miss behavior is additionally taken into this issue,eth_uninstallFilterreturningfalseinstead of an error, avoiding a separate intermediate policy for uninstall lookup misses. The other behavior changes stay out of scope.-32601, so the parameters never reach the business logic-32602on null (most of these null checks were added by #6828)NullPointerExceptionon nullweb3_sha3(null)returns the hash of empty input; whether the transaction index is validated depends on whether the block exists; the 4 uncle methods validate nothing; an optional block parameter that is null returns an error (-32600foreth_call) instead of being treated aslatestOn the baseline verified here, the node logs nothing for these calls, because
JsonRpcServletsetssetShouldLogInvocationErrors(false)and the fallback path has no log point of its own. develop @ 4a21592 and GreatVoyage-v4.8.2.1 are both affected; verified on Java 8 and Java 17. Reproduce:Limitations and Risks
-32001/-32000) on these paths will observe a change.Proposed Solution
Proposed Design
Basis, in priority order: the Execution API
required/ schema; whethernullcan reasonably be taken as the zero value; go-ethereum (v1.17.6) / Besu (26.9.0) behavior as a reference.The null-input results below apply on endpoints where the methods are available. Existing request-source checks retain precedence; unavailable methods keep their current errors.
-32602, messageinvalid filter request(filter methods) /invalid paramsrequired. go-ethereum v1.17.6 and Besu 26.9.0 also reject it with-32602; go-ethereum v1.17.5 and earlier decoded it as a zero value. The comparison applies to the foureth_methods; the TRON-specificbuildTransactionfollows the same required-object policy.eth_uninstallFilter/eth_getFilterChanges/eth_getFilterLogs-32602 "invalid params"nullhere with-32602; go-ethereum v1.17.5 and earlier decoded it as an empty IDeth_uninstallFilterwith a non-null ID that is unknown or already removedfalseUninstallFilterreports whether a filter was found; removing an installed filter still returnstrueeth_getFilterChanges/eth_getFilterLogswith a non-null unknown ID-32000 "filter not found", unchangedfullTransactionObjects-32602 "invalid params". The block hash or selector is validated first, and the flag is checked before any block lookup, so the result does not depend on whether the block existsnullhere with-32602; go-ethereum v1.17.5 and earlier decoded it asfalseCallArguments.from, go-ethereum (a pointer field) and Besu behave the same wayError responses keep the existing annotation mapping:
datais"{}"and theidis echoed.Key Changes
eth_getBlockByHash/eth_getBlockByNumber, the block hash or selector is validated first, thenfullTransactionObjects, before any block lookup; a null flag is no longer unboxed.@JsonSetter(nulls = Nulls.SKIP)to the 7 DTO fields; all 7 already declare non-null default initializers (0L/0/""/ the zero address), so skipping the setter on an explicit null lands exactly on the omitted-field semantics.eth_callvalidates its required transaction argument before the block parameter, soeth_call([null, null])goes from-32600to-32602.frameworkmodule:TronJsonRpc,TronJsonRpcImpl,JsonRpcApiUtil,LogFilter,BuildArguments,CallArguments. Remove the obsoleteItemNotFoundExceptiondeclaration and mapping frometh_uninstallFilter; the other filter methods retain them. Removing athrowsclause keeps existing Java binaries compatible, but source callers that specifically catch that checked exception, or implementations that still declare it, may need adjustment when recompiled. OnTronJsonRpcImpl,uninstallFilterandgetFilterChangesalso declareJsonRpcInvalidParamsException, whichTronJsonRpcalready declares for them.Impact
Compatibility
fullTransactionObjects-32001->-32602;eth_getBlockByNumberwith a non-existent block and a nullfullTransactionObjectsgoes fromresult: nullto-32602;eth_uninstallFilterreturnsfalsefor any non-null ID that does not identify an installed filter, replacing the previous-32000; explicit null DTO fields equal omission;eth_call([null, null])goes from-32600to-32602.eth_uninstallFilteranswers with an error or with a result.ByteArray.fromHexonly strips a0xprefix and left-pads to an even length, so it normalizes rather than validates. Once a lookup miss returnsfalse, an empty string or a non-hexadecimal string also returnsfalseinstead of-32000, because they simply fail the lookup. This issue does not add format validation as a side effect.Every
-32001above is jsonrpc4j's fallback for an exception without an@JsonRpcErrorsmapping. If #6941 lands first, that fallback becomes-32603 "Internal error"withoutdata, so only the observed "before" side of these rows changes; the target behavior defined by this issue is the same either way.The following remain unchanged: results of valid non-null requests other than the
eth_uninstallFilterlookup misses above, HTTP status codes, the request-source check, wildcard semantics of nulls inside a filter object, gRPC and non-JSON-RPC HTTP API behavior.Acceptance Criteria
JsonRpcServer; error assertions covercode,messageanddata, including the absence of Java exception class names.ObjectMapperdeserialization: an explicit null and an omitted field give the same result.-32602 "invalid params"from all three filter-ID methods on supported endpoints; PBFT retains-32601.fullTransactionObjectsreturns-32602 "invalid params"for existing and non-existent blocks; an invalid block hash or selector keeps its own error, and a null flag is rejected before any block is read.eth_uninstallFilterreturnsfalsefor an unknown ID and a second removal of the same ID, andtrueonly when an installed filter is removed.eth_uninstallFilterreturnsfalsefor an empty string and for a non-hexadecimal string, and no format validation is introduced.Follow-up
Outside the scope of this issue and not blocking its closure:
params,params: nullandparams: []retain existing dispatch and arity behavior and are not universally rejected: whether they are accepted depends on how many parameters the method declares. This issue covers explicitnullvalues in the argument positions and DTO fields listed above, plus theeth_uninstallFilterlookup-miss result.web3_sha3(null), unconditional transaction index validation, parameter validation for the uncle methods, and normalizing an omitted / null optional block parameter tolatest; each gets its own issue.Additional Notes
frameworkmodule (TronJsonRpc,TronJsonRpcImpland the related DTOs) and does not touch consensus, transaction execution or other core logic. [Feature]Standardize JSON-RPC error handling(revert codes, LiteNode pruned-history responses, request fields validation) #6676 also touchesJsonRpcApiUtilandTronJsonRpcImpl, and [Feature] Standardize JSON-RPC error mapping and exception boundaries #6941 also modifiesTronJsonRpcandTronJsonRpcImpl. This issue does not require [Feature]Standardize JSON-RPC error handling(revert codes, LiteNode pruned-history responses, request fields validation) #6676 or [Feature] Standardize JSON-RPC error mapping and exception boundaries #6941 to land first; whichever lands second rebases and re-runs the related regression tests.