[Feature] Standardize JSON-RPC null parameter handling
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 30/100
Research direction
Read the JSON-RPC layer in TronJsonRpc, TronJsonRpcImpl, JsonRpcApiUtil, LogFilter, BuildArguments, and CallArguments. Exercise the listed inputs through a real JsonRpcServer and verify DTO behavior with ObjectMapper; done means the acceptance criteria pass without changing non-JSON-RPC behavior.
Written by the indexing model from the issue text.
Description
Summary
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:
{"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":null,"data":"java.lang.NullPointerException"}}
This issue defines the null handling of 10 parameter positions and 7 optional DTO fields:
- A required object parameter that is null returns
-32602. - A null filter ID or
fullTransactionObjectsfollows go-ethereum's result. - An optional DTO field explicitly set to
nullis treated as omitted.
This issue handles the listed null inputs and aligns eth_uninstallFilter lookup-miss results 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: 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.- 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 dereference the argument without a null check:
- null object parameter:
eth_getLogs,eth_newFilter,eth_estimateGas,eth_call,buildTransaction - null filter ID:
eth_uninstallFilter,eth_getFilterChanges,eth_getFilterLogs - null
fullTransactionObjects(Booleanauto-unboxing):eth_getBlockByHash,eth_getBlockByNumber
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 leads to an unhandled exception, or DTO fields whose explicit null differs from omission; 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 that 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 -32602 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 @ 4a21592f95 and GreatVoyage-v4.8.2.1 are both affected; verified on Java 8 and Java 17. Reproduce:
curl -s -X POST http://127.0.0.1:8545/jsonrpc -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"eth_getLogs","params":[null],"id":1}'
Limitations and Risks
- 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 / Besu behavior as a reference.
| Input | Handling | Basis |
|---|---|---|
| Null required object parameter (5 methods) | -32602, message invalid filter request (filter methods) / invalid params |
JSON-RPC 2.0 / Execution API required; deliberately stricter than go-ethereum, which accepts it through zero-value decoding. The Execution API and go-ethereum comparison applies to the four eth_ methods; the TRON-specific buildTransaction follows the same required-object policy. |
eth_uninstallFilter with a null ID, an unknown ID, or an ID 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([null]) / eth_getFilterLogs([null]) |
-32000 "filter not found" |
aligned with go-ethereum |
Null fullTransactionObjects |
treated as false, the block is returned with transaction hashes |
aligned with go-ethereum (zero value); both values denote the same block, a compatibility extension beyond the schema |
| Optional DTO field explicitly null (7 fields) | same as omitting the field | the zero value is the field default; CallArguments.from aligned with go-ethereum |
Error responses keep the existing annotation mapping: data is "{}" and the id is echoed.
Key Changes
- Null-check the 10 positions at the method entry, after the request-source check and before the business logic;
Boolean.TRUE.equals(...)removes the unboxing offullTransactionObjects. - 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_callvalidates its required transaction argument before the block parameter, soeth_call([null, null])goes from-32600to-32602.- The change is limited to the JSON-RPC layer of the
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.
Impact
- Security: no confidentiality / integrity / availability impact.
- 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. Object parameters -32001 -> -32602; get-filter methods -32001 -> -32000; eth_uninstallFilter returns false for a null ID and for any ID that does not identify an installed filter, replacing the previous -32001 and -32000; eth_getBlockBy*(..., null) goes from an error to a normal response; 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 covercode,messageanddata, including the absence of Java exception class names. - DTO fields are verified through
ObjectMapperdeserialization: an explicit null and an omitted field give the same result. - The request-source check on a SolidityNode / under PBFT is unchanged.
-
eth_uninstallFilterreturnsfalsefor a null ID, 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. - 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: 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. - 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 tolatest; each gets its own issue.
Additional Notes
- Do you have ideas regarding implementation? Yes. The implementation and regression tests are complete, including the
eth_uninstallFilterlookup-miss change and the checked-exception removal discussed below, and are verified locally on JDK 17; the PR follows once this issue is confirmed. - Are you willing to implement this feature? Yes.
- PR #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.
- The change is confined to the JSON-RPC layer of the
frameworkmodule (TronJsonRpc,TronJsonRpcImpland the related DTOs) and does not touch consensus, transaction execution or other core logic. #6676 also touchesJsonRpcApiUtilandTronJsonRpcImpl, and #6941 also modifiesTronJsonRpcandTronJsonRpcImpl. This issue does not require #6676 or #6941 to land first; whichever lands second rebases and re-runs the related regression tests.
- Dominant language
- Java
- Stars
- 4.2k
- Forks
- 1.7k
- Avg merge
- 6d 20h
- Merged PRs (30d)
- 14
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from tronprotocol/java-tron
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
tronprotocol/java-tron#6969 · 8 comments ·
-
type:feature
Difficulty 5/5 Over a week Newbie friendliness 48/100
tronprotocol/java-tron#6963 · 6 comments ·
-
type:feature
Difficulty 5/5 Over a week Newbie friendliness 28/100
tronprotocol/java-tron#6959 · 3 comments ·
-
type:feature
Difficulty 5/5 Over a week Newbie friendliness 38/100
tronprotocol/java-tron#6958 · 3 comments ·
-
topic:release type:tracking
Difficulty 4/5 3-5 days Newbie friendliness 35/100
tronprotocol/java-tron#6957 · 2 comments ·
All issues in tronprotocol/java-tron
Similar issues
-
Bug Java Platform: Java
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
getsentry/sentry-java#6138 · 1 comment ·
-
bug needs triage p2
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
GoogleCloudPlatform/DataflowTemplates#4273 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
bug needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100