Visitar URL original
feat(jsonrpc): return code 3 for revert and 4444 for pruned history by 0xbigapple · Pull Request #7004 · tronprotocol/java-tron · GitHub
Skip to content

feat(jsonrpc): return code 3 for revert and 4444 for pruned history - #7004

Merged
kuny0707 merged 12 commits into
tronprotocol:release_v4.8.3from
0xbigapple:feature/jsonrpc-revert-and-pruned-history
Oct 15, 2026
Merged

kuny0707 merged 12 commits into
tronprotocol:release_v4.8.3from
0xbigapple:feature/jsonrpc-revert-and-pruned-history

Conversation

@0xbigapple

@0xbigapple 0xbigapple commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

First of two PRs for #6676; request validation is split out (see Follow up).

What does this PR do?

Aligns two JSON-RPC error behaviors with the Ethereum Execution API:

  1. Contract revert returns code 3. eth_call and eth_estimateGas return code: 3 instead of -32000; other execution failures stay -32000. Wallet.callConstantContract now records contractRet on the result, so the JSON-RPC layer classifies on the enum rather than on the error message.

  2. Pruned history returns 4444 "Pruned history unavailable".

    • Block-body methods (eth_getBlockByNumber, eth_getBlockTransactionCountByNumber, eth_getTransactionByBlockNumberAndIndex): on a LiteNode, heights below the lowest stored block. Genesis stays queryable.
    • Receipt and log methods (eth_getBlockReceipts, eth_getLogs, eth_newFilter), including when the block is selected by hash: on any node when transHistory.switch = off; otherwise on a LiteNode, heights below the lowest receipt block (the first block in transactionRetStore, read at startup). A FullNode has no such check — receipts from before June 2019 may still be served from TransactionHistoryStore, so the store's first key is not a lower bound — and receipt gaps on a FullNode keep the current behavior.
    • As in geth, 4444 carries no block number; data is the "{}" placeholder every java-tron JSON-RPC error has.
    • earliest resolves to the lowest block with all persisted data: on a LiteNode the lowest receipt block (the lowest stored block when receipts are not persisted), on a FullNode 0.

Why are these changes required?

  • Under -32000, a contract revert cannot be told apart from out-of-energy or any other TVM failure.
  • A LiteNode returns null for pruned blocks, which reads as "block does not exist", and eth_getLogs across the cutoff returns incomplete results without an error.

Breaking changes (release-note items):

  • eth_call / eth_estimateGas: a contract revert returns 3 instead of -32000.
  • LiteNode: heights below the lowest available block return 4444 instead of null on block-body methods, and instead of -32000 or incomplete results on receipt and log methods.
  • Any node with transHistory.switch = off: receipt and log queries return 4444, including for receipts already on disk.
  • earliest on a LiteNode resolves to the lowest available block instead of 0.
  • eth_getTransactionByBlockNumberAndIndex / eth_getTransactionByBlockHashAndIndex: an invalid index returns -32602 instead of null when the block is missing or pruned.

This PR has been tested by:

  • Unit tests — revert classification, the pruned-history rules on both node types with the switch on and off, reading the lowest receipt block at startup (through context startup, and with the switch off), the 4444 / 3 mappings on every affected TronJsonRpc method, and resolver declaration-order independence.
  • Manual testing — 141 JSON-RPC cases against the full and solidity endpoints, on Nile and mainnet LiteNode snapshots (switch on and off) and on a private-chain FullNode (switch on, off, and the receipt gap left after turning it back on). After the review fixes, re-run on a private-chain FullNode and a private LiteNode snapshot (switch on and off): 106/106.

Follow up

Extra details

  • The solidity JSON-RPC endpoint follows the same rules.
  • Side effect (not breaking): gRPC/HTTP triggerConstantContract responses now carry contractRet: REVERT when the call reverts, and so does triggerSmartContract when its selector is a constant ABI function.
  • Every REVERT returns 3, including one with empty revert data.
  • Only single-block and genesis-only queries are exempt for genesis: eth_getLogs from 0x0 to a later block on a LiteNode returns 4444, as geth does.
  • Hash-based selectors, refining "null-only" from [Feature]Standardize JSON-RPC error handling(revert codes, LiteNode pruned-history responses, request fields validation) #6676: a pruned block cannot be found by hash, so a pruned hash cannot be told apart from an unknown one, and those lookups keep their current response: null, or -32602 invalid blockHash on eth_getLogs / eth_newFilter. When eth_getBlockReceipts or eth_getLogs does find the block by hash, its number is known and the receipt rules above apply. eth_getBlockByHash, eth_getTransactionByHash and eth_getTransactionReceipt are unchanged.

Known limitations

  • On a LiteNode, blocks between the lowest stored block and the lowest receipt block are served, but receipt-derived fields read as zero: block gasUsed and transaction gas. Clients cannot tell missing receipt data from actual zero usage. This follows from the availability rules above and goes away once the Toolkit follow-up aligns the two boundaries.

- eth_call / eth_estimateGas: contract revert changes from -32000 to 3
- data a node does not have (pruned on a LiteNode, or receipts with
  transHistory.switch off) returns 4444 "Pruned history unavailable"
- 4444 no longer carries the earliest available block in data, as geth
- remove ResultCodeUtil; constant calls record only REVERT
- test that a reverted constant call sets contractRet to REVERT
…ove init methods to the top

- rename probeLowestBlockNumOfReceiptStore to initLowestBlockNumOfReceiptStore
- move init and initLowestBlockNumOfReceiptStore to the top of ChainBaseManager methods
@github-actions
github-actions Bot requested a review from bladehan1 September 28, 2026 13:11
@0xbigapple
0xbigapple requested review from 317787106 and waynercheung and removed request for bladehan1 September 28, 2026 13:11
@halibobo1205 halibobo1205 added this to the GreatVoyage-v4.8.3 milestone Sep 29, 2026
@317787106

Copy link
Copy Markdown
Collaborator

A few points from the parameter-handling review. These issues predate this PR, but the new 4444 path also interacts with the validation order:

  1. Validate eth_call block objects consistently.
    A numeric selector such as "0x64" is rejected, while {"blockNumber":"0x64"} only checks that the block exists and then silently switches execution to latest. Supplying both blockNumber and blockHash, or a non-boolean requireCanonical, is also accepted. If historical execution is unsupported, please reject these selectors consistently and validate the object’s fields instead of silently ignoring them.
  2. Preserve invalid-parameter errors in eth_estimateGas.
    args.parseValue() runs inside a broad catch (Exception), which converts JsonRpcInvalidParamsException into JsonRpcInternalException. Consequently, malformed, negative, or overflowing values return -32000, whereas the same values in eth_call return -32602. Please parse these parameters before the execution block, or rethrow JsonRpcInvalidParamsException unchanged.
  3. Validate transaction indices before block lookup and pruning checks.
    Both transaction-by-block-and-index methods currently validate the index only after finding the block. An invalid index therefore returns -32602 for an existing block, null for a missing block, and now 4444 for a pruned height. Please validate the selector and index syntax before querying availability.

Some optional naming improvements:

  • earliestAvailableBlock → resolveEarliestBlockNum: makes both the tag-resolution role and numeric return value explicit.
  • checkPrunedHistory → checkPrunedBlockHistory: distinguishes the block-body check from checkPrunedReceiptHistory.
  • bnOrId → blockNumOrTag: matches the implementation and accepted selector types.
  • getABIOfSmartContract → getContractCode: the existing method implements eth_getCode and returns runtime bytecode, rather than an ABI.

The existing issues outside this PR’s scope can be handled in this PR or follow-ups.

@0xbigapple

0xbigapple commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

A few points from the parameter-handling review. These issues predate this PR, but the new 4444 path also interacts with the validation order:

  1. Validate eth_call block objects consistently.
    A numeric selector such as "0x64" is rejected, while {"blockNumber":"0x64"} only checks that the block exists and then silently switches execution to latest. Supplying both blockNumber and blockHash, or a non-boolean requireCanonical, is also accepted. If historical execution is unsupported, please reject these selectors consistently and validate the object’s fields instead of silently ignoring them.
  2. Preserve invalid-parameter errors in eth_estimateGas.
    args.parseValue() runs inside a broad catch (Exception), which converts JsonRpcInvalidParamsException into JsonRpcInternalException. Consequently, malformed, negative, or overflowing values return -32000, whereas the same values in eth_call return -32602. Please parse these parameters before the execution block, or rethrow JsonRpcInvalidParamsException unchanged.
  3. Validate transaction indices before block lookup and pruning checks.
    Both transaction-by-block-and-index methods currently validate the index only after finding the block. An invalid index therefore returns -32602 for an existing block, null for a missing block, and now 4444 for a pruned height. Please validate the selector and index syntax before querying availability.

Some optional naming improvements:

  • earliestAvailableBlock → resolveEarliestBlockNum: makes both the tag-resolution role and numeric return value explicit.
  • checkPrunedHistory → checkPrunedBlockHistory: distinguishes the block-body check from checkPrunedReceiptHistory.
  • bnOrId → blockNumOrTag: matches the implementation and accepted selector types.
  • getABIOfSmartContract → getContractCode: the existing method implements eth_getCode and returns runtime bytecode, rather than an ABI.

The existing issues outside this PR’s scope can be handled in this PR or follow-ups.

@317787106 Thanks.
3. Fixed: the index is now validated before the block lookup.
1, 2. Will handle in follow-ups.
Naming: renamed earliestAvailableBlock and checkPrunedHistory as suggested; bnOrId and getABIOfSmartContract are pre-existing and left as is.

…sonrpc-revert-and-pruned-history

# Conflicts:
#	framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java
#	framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java
#	framework/src/test/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolverTest.java

@waynercheung waynercheung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked 66b53b5 against release_v4.8.3 at 7ebc2eb.

The merge preserves the relevant validation order: eth_getBlockByNumber validates fullTransactionObjects before the pruning check, and both by-index methods validate the index before lookup. The receipt-floor rules and direct JsonRpcException inheritance match the design settled in #6676, and the 4444 response keeps the specified message without cutoff metadata.

I did not find a blocking correctness issue. Please address the two test gaps noted inline and make sure CI passes on the final head before approval.

[SHOULD] Please document the retained-body receipt gap as a known limitation. Between the body floor and the receipt floor, body queries still return receipt-derived fields as zero (block gasUsed, transaction gas). This is existing behavior under the agreed availability policy, but clients cannot tell missing receipt data from actual zero usage.

[SHOULD] Please update the reference.conf / config.conf comments to say that transHistory.switch = off also disables receipt/log queries even when receipts remain on disk (genesis-only queries excepted). The PR description already lists this change; please carry it into the release notes.

[QUESTION] The revert message stays REVERT opcode executed[: reason], while geth uses execution reverted[: reason]. Is keeping it intentional? If so, please state it in the description.

[QUESTION] Could you link the tracking issues for the deferred eth_call block-object validation, estimateGas parameter-error handling, Toolkit receipt retention, and the docs-en / docs-zh error-code updates?

[NIT]

  • TransactionRetStore.getLowestBlockNum() shares its name with ChainBaseManager.getLowestBlockNum() / Wallet.getLowestBlockNum(), which mean the body floor; getLowestReceiptBlockNum() would avoid the ambiguity.
  • Description: contractRet: REVERT also reaches triggersmartcontract when its selector is a constant ABI function.
  • Description: pin or remove the empty-revert comparison with geth; v1.17.5 returns code 3 for empty revert data.
  • Description: narrow the unknown-hash "null" wording; eth_getLogs / eth_newFilter keep returning -32602 invalid blockHash.

- rename TransactionRetStore.getLowestBlockNum to getLowestReceiptBlockNum
- note that transHistory.switch off disables receipt and log queries
@0xbigapple

0xbigapple commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator Author

Re-checked 66b53b5 against release_v4.8.3 at 7ebc2eb.

The merge preserves the relevant validation order: eth_getBlockByNumber validates fullTransactionObjects before the pruning check, and both by-index methods validate the index before lookup. The receipt-floor rules and direct JsonRpcException inheritance match the design settled in #6676, and the 4444 response keeps the specified message without cutoff metadata.

I did not find a blocking correctness issue. Please address the two test gaps noted inline and make sure CI passes on the final head before approval.

[SHOULD] Please document the retained-body receipt gap as a known limitation. Between the body floor and the receipt floor, body queries still return receipt-derived fields as zero (block gasUsed, transaction gas). This is existing behavior under the agreed availability policy, but clients cannot tell missing receipt data from actual zero usage.

[SHOULD] Please update the reference.conf / config.conf comments to say that transHistory.switch = off also disables receipt/log queries even when receipts remain on disk (genesis-only queries excepted). The PR description already lists this change; please carry it into the release notes.

[QUESTION] The revert message stays REVERT opcode executed[: reason], while geth uses execution reverted[: reason]. Is keeping it intentional? If so, please state it in the description.

[QUESTION] Could you link the tracking issues for the deferred eth_call block-object validation, estimateGas parameter-error handling, Toolkit receipt retention, and the docs-en / docs-zh error-code updates?

[NIT]

  • TransactionRetStore.getLowestBlockNum() shares its name with ChainBaseManager.getLowestBlockNum() / Wallet.getLowestBlockNum(), which mean the body floor; getLowestReceiptBlockNum() would avoid the ambiguity.
  • Description: contractRet: REVERT also reaches triggersmartcontract when its selector is a constant ABI function.
  • Description: pin or remove the empty-revert comparison with geth; v1.17.5 returns code 3 for empty revert data.
  • Description: narrow the unknown-hash "null" wording; eth_getLogs / eth_newFilter keep returning -32602 invalid blockHash.

@waynercheung Thanks for the re-check.

  • Test gaps: addressed inline.
  • Receipt gap: added under Known limitations in the description.
  • Config comments: updated reference.conf and config.conf; the release-note line is already under Breaking changes.
  • Revert message: intentional for now. REVERT opcode executed is the TVM's own revert message and already states the failure clearly, so we keep it.
  • Tracking: the eth_call block object and the eth_estimateGas value error predate this PR and are out of its scope; they are recorded in #6676. Toolkit receipts: will open a separate issue. The docs-en / docs-zh error-code pages will be updated with the 4.8.3 release.
  • Renamed TransactionRetStore.getLowestBlockNum() to getLowestReceiptBlockNum().
  • Description NITs: fixed.

@0xbigapple
0xbigapple requested review from lvs0075 and lxcmyf October 14, 2026 02:58
@lvs0075
lvs0075 removed their request for review October 14, 2026 03:03
@lvs0075
lvs0075 removed their request for review October 14, 2026 10:02

@waynercheung waynercheung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed 6e84880 and tested a local merge with release_v4.8.3 at 4db3688. The merge is clean.

Both previous test gaps are addressed. On the merged tree, removing the 4444 mappings, removing the 3 mappings, removing the Manager init call, or replacing Long.MAX_VALUE with getHeadBlockNum() + 1 each fails a test.

I rechecked the revert classification, receipt-floor initialization, pruning boundaries and validation order, and found no blocking issues. GitHub CI for the PR head is green.

[NIT] Please link the Toolkit receipt-retention issue once it is open, and update the request-envelope follow-up to reference the merged PR #7032.

Approved.

@0xbigapple
0xbigapple requested a review from 3for October 15, 2026 05:34
- run the constant revert test with debug on to skip the cpu time limit
- clarify transHistory.switch comment in reference.conf and config.conf
- drop unused throws from getFilterChanges

@lxcmyf lxcmyf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the revert classification and pruned-history handling on 6e84880, including startup receipt-floor initialization, body/receipt/log cutoff boundaries, genesis exceptions, error-code resolution, and validation order. Targeted tests and static security scans pass. No additional blocking issues found.

@0xbigapple

Copy link
Copy Markdown
Collaborator Author

Re-reviewed 6e84880 and tested a local merge with release_v4.8.3 at 4db3688. The merge is clean.

Both previous test gaps are addressed. On the merged tree, removing the 4444 mappings, removing the 3 mappings, removing the Manager init call, or replacing Long.MAX_VALUE with getHeadBlockNum() + 1 each fails a test.

I rechecked the revert classification, receipt-floor initialization, pruning boundaries and validation order, and found no blocking issues. GitHub CI for the PR head is green.

[NIT] Please link the Toolkit receipt-retention issue once it is open, and update the request-envelope follow-up to reference the merged PR #7032.

Approved.

@waynercheung Thanks. The request-envelope follow-up now references #7032. I'll link the Toolkit issue once it is open.

@kuny0707
kuny0707 merged commit 3b35e94 into tronprotocol:release_v4.8.3 Oct 15, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

10 participants