Skip to content

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

Open
0xbigapple wants to merge 1 commit into
release_v4.8.3from
feature/jsonrpc-revert-and-pruned-history
Open

0xbigapple wants to merge 1 commit into
release_v4.8.3from
feature/jsonrpc-revert-and-pruned-history

Conversation

@0xbigapple

@0xbigapple 0xbigapple commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

First of two PRs for tronprotocol#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 receipt floor (the first block in transactionRetStore, probed at startup). A FullNode has no floor check — receipts from before June 2019 may still be served from TransactionHistoryStore, so the store's first key is not a floor — and receipt gaps on a FullNode keep the current behavior.
    • data carries the earliest available block as hex, the same value earliest resolves to. With receipts not persisted there is no floor, and data is the usual "{}" placeholder.
    • earliest resolves to the lowest block with all persisted data: on a LiteNode the receipt floor (the body floor 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 pruning floor return 4444 instead of null on block-body methods, and instead of -32000 or incomplete results on receipt and log methods.
  • FullNode with transHistory.switch = off: receipt and log queries return 4444.
  • earliest on a LiteNode resolves to the pruning floor 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, the startup receipt-floor probe, and resolver declaration-order independence. The jsonrpc test packages are green on release_v4.8.3.
  • 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).

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.
  • The block number in the 4444 data is a TRON extension; the Execution API, geth and Besu send only code and message.
  • Side effect (not breaking): gRPC/HTTP triggerConstantContract responses now carry contractRet.
  • 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) tronprotocol/java-tron#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.

Summary by cubic

Aligns JSON-RPC error responses with the Ethereum Execution API by distinguishing contract reverts from other failures and by surfacing pruned history instead of returning empty results.

  • eth_call and eth_estimateGas return code 3 with the revert data for a contract revert; instead of -32000 for all failures.
  • eth0 for non-revert failures remains on the same as before.
  • Blocked blocks below the persisted data floor on a LiteNode return 4004 and {height} so are handles with more data.

Migrating

  • Contract revert responses now use code 3; clients may continue to treat revert as -1.
  • The transaction etc.

We'll produce a brief description with articles.

However, the expected output should be concise and not too long. Let's probably use 2 sections.

Given the PR is large, we have 2 sections permitted. We'll have "behaviour" and "breaking changes".

Thus, I'll produce:

  • Behavior changes - Two new facts.
  • Breaking changes - List of items.

We'll do that.

Let's ensure we mention all the changes.

  • The LiteLLMeth0 and1.

We'll include the endpoints.

We'll also mention the eth0.

Now, craft final output.

We'll note "The data field now carries the earliest available block for outright but also important".

Let's write.

But writing includes a bit more details.

Let's write a draft.

The PR description should be about 2-3 paragraphs. We'll include bulletsI'll present the PRD now.

No changes the requirement.

Written for commit c164330. Summary will update on new commits.

Review in cubic

- 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"
@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: c9777253-f6c8-461b-9d6a-74397a6a0a73

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 23 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="framework/src/main/java/org/tron/core/utils/ResultCodeUtil.java">

<violation number="1" location="framework/src/main/java/org/tron/core/utils/ResultCodeUtil.java:12">
P3: This method duplicates the full exception-to-`contractResult` mapping in `RuntimeImpl.setResultCode`, so future TVM result additions can update one path while constant calls silently return `UNKNOWN`. Keep success/revert handling local, but share the exception classifier between both callers.</violation>
</file>

<file name="common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcPrunedHistoryException.java">

<violation number="1" location="common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcPrunedHistoryException.java:5">
P3: This javadoc says code 4444 is standardized by the Ethereum Execution API (EIP-4444), but it is a TRON-specific code — EIP-4444 specifies historical data expiry, not JSON-RPC error codes, and the PR description itself notes the 4444 extension. Reword to avoid claiming Ethereum standardization, e.g. 'returns JSON-RPC error code 4444 for pruned history (EIP-4444-inspired TRON extension)'.</violation>
</file>

<file name="chainbase/src/main/java/org/tron/core/store/TransactionRetStore.java">

<violation number="1" location="chainbase/src/main/java/org/tron/core/store/TransactionRetStore.java:51">
P1: This limits the scan before filtering key shape, so an earlier non-block key can hide all valid receipt keys and make startup classify existing history as pruned. Scan past non-8-byte keys, or enforce and validate a block-number-only key invariant before applying the limit.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

* layers, {@code getNext} does not merge deletions correctly.
*/
public OptionalLong getLowestBlockNum() {
Map<byte[], byte[]> entries = revokingDB.getNext(ByteArray.fromLong(0), 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: This limits the scan before filtering key shape, so an earlier non-block key can hide all valid receipt keys and make startup classify existing history as pruned. Scan past non-8-byte keys, or enforce and validate a block-number-only key invariant before applying the limit.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At chainbase/src/main/java/org/tron/core/store/TransactionRetStore.java, line 51:

<comment>This limits the scan before filtering key shape, so an earlier non-block key can hide all valid receipt keys and make startup classify existing history as pruned. Scan past non-8-byte keys, or enforce and validate a block-number-only key invariant before applying the limit.</comment>

<file context>
@@ -37,6 +40,23 @@ public void put(byte[] key, TransactionRetCapsule item) {
+   * layers, {@code getNext} does not merge deletions correctly.
+   */
+  public OptionalLong getLowestBlockNum() {
+    Map<byte[], byte[]> entries = revokingDB.getNext(ByteArray.fromLong(0), 1);
+    for (byte[] key : entries.keySet()) {
+      if (key.length == Long.BYTES) {
</file context>

*/
public class ResultCodeUtil {

public static contractResult resolve(RuntimeException exception) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This method duplicates the full exception-to-contractResult mapping in RuntimeImpl.setResultCode, so future TVM result additions can update one path while constant calls silently return UNKNOWN. Keep success/revert handling local, but share the exception classifier between both callers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At framework/src/main/java/org/tron/core/utils/ResultCodeUtil.java, line 12:

<comment>This method duplicates the full exception-to-`contractResult` mapping in `RuntimeImpl.setResultCode`, so future TVM result additions can update one path while constant calls silently return `UNKNOWN`. Keep success/revert handling local, but share the exception classifier between both callers.</comment>

<file context>
@@ -0,0 +1,48 @@
+ */
+public class ResultCodeUtil {
+
+  public static contractResult resolve(RuntimeException exception) {
+    if (exception instanceof Program.IllegalOperationException) {
+      return contractResult.ILLEGAL_OPERATION;
</file context>


/**
* Thrown when a request targets historical state that a LiteNode has pruned.
* Maps to JSON-RPC error code 4444 "Pruned history unavailable", as standardized

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This javadoc says code 4444 is standardized by the Ethereum Execution API (EIP-4444), but it is a TRON-specific code — EIP-4444 specifies historical data expiry, not JSON-RPC error codes, and the PR description itself notes the 4444 extension. Reword to avoid claiming Ethereum standardization, e.g. 'returns JSON-RPC error code 4444 for pruned history (EIP-4444-inspired TRON extension)'.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcPrunedHistoryException.java, line 5:

<comment>This javadoc says code 4444 is standardized by the Ethereum Execution API (EIP-4444), but it is a TRON-specific code — EIP-4444 specifies historical data expiry, not JSON-RPC error codes, and the PR description itself notes the 4444 extension. Reword to avoid claiming Ethereum standardization, e.g. 'returns JSON-RPC error code 4444 for pruned history (EIP-4444-inspired TRON extension)'.</comment>

<file context>
@@ -0,0 +1,17 @@
+
+/**
+ * Thrown when a request targets historical state that a LiteNode has pruned.
+ * Maps to JSON-RPC error code 4444 "Pruned history unavailable", as standardized
+ * by the Ethereum Execution API (EIP-4444).
+ */
</file context>
Suggested change
* Maps to JSON-RPC error code 4444 "Pruned history unavailable", as standardized
* Maps to JSON-RPC error code 4444 "Pruned history unavailable", a TRON extension
* inspired by EIP-4444 historical data expiry.

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.

1 participant