Sitelet https://github.com/tronprotocol/java-tron/pull/7004
Skip to content

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

Open
0xbigapple wants to merge 5 commits into
tronprotocol:release_v4.8.3from
0xbigapple:feature/jsonrpc-revert-and-pruned-history
Open

0xbigapple wants to merge 5 commits into
tronprotocol:release_v4.8.3from
0xbigapple:feature/jsonrpc-revert-and-pruned-history

Conversation

@0xbigapple

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.

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, 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

  • Request-envelope validation (jsonrpc version, non-string method, explicit id: null) in a new PR.
  • Toolkit: retain the snapshot window's receipts so bodies and receipts share one boundary.

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.
  • Every REVERT returns 3, including one with empty revert data; geth returns -32000 in that case.
  • 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 invalid one, and those lookups keep returning null. 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.

- 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants