Skip to content

fix(jsonrpc): accept "input" as alias for "data" in call args#6722

Open
waynercheung wants to merge 2 commits intotronprotocol:developfrom
waynercheung:fix/jsonrpc-eth-call-input-param-6517
Open

fix(jsonrpc): accept "input" as alias for "data" in call args#6722
waynercheung wants to merge 2 commits intotronprotocol:developfrom
waynercheung:fix/jsonrpc-eth-call-input-param-6517

Conversation

@waynercheung
Copy link
Copy Markdown
Collaborator

@waynercheung waynercheung commented Apr 28, 2026

What does this PR do?

Accept input alongside data on eth_call, eth_estimateGas, and buildTransaction call arguments, matching the Ethereum execution-apis spec and resolving #6517.

  • Add input (raw field) to CallArguments and BuildArguments.
  • Resolve calldata via resolveData() - pure precedence resolution with input winning over data, mirroring go-ethereum's TransactionArgs.data().
  • On the build path, conflicts between input and data (both set, not equal) are detected at BuildArguments.getContractType() - mirroring geth's data() / setDefaults split. The error message uses go-ethereum's wording verbatim. The query path stays lenient (input wins silently).
  • Switch five production read sites in TronJsonRpcImpl from getData() to resolveData().
  • Hex validation is mode-driven via JsonRpcApiUtil.requireValidHex (HexMode.STRICT / HexMode.LENIENT):
    • input is STRICT: follows the execution-apis BYTES schema (^0x[0-9a-f]*$, even length; "" is accepted as empty bytes per geth's hexutil.Bytes.UnmarshalText).
    • data keeps LENIENT parsing via ByteArray.fromHexString (accepts bare hex and odd length) for backward compatibility with existing callers - notably BuildTransactionTest.testCreateSmartContract, which submits bare-hex bytecode.
  • The resolver is named resolveData() - verb prefix, not getXxx - so Jackson's and FastJSON's JavaBean introspection skips it with no per-serializer annotations needed.
  • The Lombok-generated positional constructors widen by one slot (CallArguments 7->8 args, BuildArguments 16->17 args). The named-JSON deserialisation path is unaffected.

Why are these changes required?

The Ethereum execution-apis spec uses input as the canonical calldata field; data is the legacy alias. go-ethereum accepts both, with input winning when equal and erroring when not. Notably, go-ethereum's ethclient has emitted only input since ethereum/go-ethereum#28078, so any tron RPC user upgrading their geth client side hits this.

Before this PR, java-tron only read data. A spec-compliant client carrying only input failed two ways:

  1. Jackson rejected the unknown property (FAIL_ON_UNKNOWN_PROPERTIES=true), returning -32603 instead of executing the call.
  2. Even when the field was tolerated, contract-creation paths (no to) tripped invalid json request because calldata appeared absent.

Both symptoms are captured in #6517.

This PR has been tested by:

  • Unit tests
  • Integration tests
  • Manual tests

CallArgumentsTest (23 tests) + BuildArgumentsTest (29 tests) = 52 total. Coverage:

  • Single-field cases: input only, data only, neither.
  • Both fields equal — precedence resolves cleanly.
  • Both fields not equal: CallArguments returns input silently; BuildArguments.getContractType() throws with geth-equivalent wording.
  • Empty-string "" is presence (matches geth).
  • 0x (zero-length payload) is presence and never collapses to absent.
  • Case-insensitive byte equality (0xDEAD == 0xdead).
  • Strict input validation rejects bare hex, odd length, non-hex chars.
  • Lenient data validation still accepts bare hex and odd length; BuildTransactionTest.testCreateSmartContract still passes.
  • Loser-field validation: malformed data is rejected even when input is the precedence winner, and vice versa.
  • Jackson + FastJSON serialisation: resolveData never appears as a wire property and serialisation never throws even with conflicting input / data (regression guard for the verb-prefix naming).
  • Are there plans to support parameter passing via the input field for eth_call? #6517 reproductions: Jackson deserialisation of {"input": "0xdeadbeef"} succeeds; contract-creation via input (no to) resolves to CreateSmartContract.

Compatibility notes

Requests sending only data with valid hex are byte-for-byte unchanged. Two error paths shift, both improvements:

  • Issue Are there plans to support parameter passing via the input field for eth_call? #6517 itself - any request carrying input. Pre-PR Jackson rejects the unknown property; jsonrpc4j surfaces this as -32700 "JSON parse error" with id: "null". Post-PR the request parses cleanly and either succeeds or returns a specific -32602 (on malformed hex or build-path conflict).

  • Invalid hex in data (e.g. data: "0xzz") - pre-PR BouncyCastle's DecoderException falls through to jsonrpc4j's default resolver:

code -32001
message "exception decoding Hex string: invalid characters encountered in Hex string"
data "org.bouncycastle.util.encoders.DecoderException"

Post-PR requireValidHex catches and rethrows as JsonRpcInvalidParamsException, mapped via @JsonRpcError to standard -32602:

code -32602
message "invalid hex string for "data""
data "{}"

Standard JSON-RPC code, deterministic message, no internal class leak. Clients hardcoded against -32700 (#6517 symptom) or -32001 (the BC leak) will see different behaviour.

Follow up

  • Add a JsonRpcServer-level integration test exercising the full Jackson -> handler -> response path for both input and data payloads. Useful but not blocking - the unit-level path is fully covered.
  • Tighten ByteArray.fromHexString to the execution-apis BYTES schema, then drop the asymmetric branch in requireValidHex. Behaviour-changing for non-RPC callers, so deferred to a separate change.

Closes tronprotocol#6517.

JSON-RPC requests using the execution-apis field name `input` were
rejected with UnrecognizedPropertyException, blocking spec-compliant
clients -- notably go-ethereum's ethclient since
ethereum/go-ethereum#28078, which only emits `input`.

CallArguments and BuildArguments now declare both fields. A new
resolveData() does pure precedence resolution -- `input` wins over
`data` -- mirroring go-ethereum's TransactionArgs.data(). Five call
sites in TronJsonRpcImpl use the resolver instead of getData(). The
verb-prefix name (not getXxx) keeps Jackson and FastJSON's JavaBean
introspection from picking the method up as a `resolveData` wire
property; two serialisation tests pin this as a regression guard.

Hex validation is mode-driven via JsonRpcApiUtil.requireValidHex
(HexMode.STRICT / HexMode.LENIENT):

- `input` (new field) is STRICT: follows the execution-apis BYTES
  schema -- requires `0x` prefix and even length; "" is accepted as
  empty bytes per geth's hexutil.Bytes.UnmarshalText.
- `data` retains LENIENT parsing via ByteArray.fromHexString for
  backward compatibility with existing callers (e.g.
  BuildTransactionTest.testCreateSmartContract uses bare-hex
  bytecode).

Conflict between `input` and `data` (both set, not equal) is
detected on the build path at BuildArguments.getContractType(),
mirroring geth's data() / setDefaults split. The error message
matches go-ethereum's setDefaults wording verbatim. Comparison is
byte-level so case differences are not flagged. The query path
(CallArguments.resolveData()) stays lenient -- input wins silently.

Tested by 52 unit tests including Jackson/FastJSON serialisation
safety guards; BuildTransactionTest.testCreateSmartContract still
passes; checkstyle clean.
@waynercheung waynercheung force-pushed the fix/jsonrpc-eth-call-input-param-6517 branch from 8df76fe to 5f8d04b Compare April 30, 2026 05:51
@0xbigapple
Copy link
Copy Markdown
Collaborator

LGTM

Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/types/BuildArguments.java Outdated
Hoist the input/data conflict error string in BuildArguments to a
private static final field CONFLICT_ERR_MSG, replacing the inline
literal in validateCallDataConflict.

The wording mirrors go-ethereum's setDefaults verbatim and is a
cross-project contract — external EVM tooling may pattern-match the
string. The constant's javadoc records this so future refactors do
not drift from the geth wording.
@waynercheung waynercheung requested a review from bladehan1 May 1, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Are there plans to support parameter passing via the input field for eth_call?

5 participants