Skip to content

fix(jsonrpc): correct QUANTITY/null format in TransactionResult#6709

Open
waynercheung wants to merge 1 commit intotronprotocol:developfrom
waynercheung:fix/jsonrpc-tx-info-quantity-format-6547
Open

fix(jsonrpc): correct QUANTITY/null format in TransactionResult#6709
waynercheung wants to merge 1 commit intotronprotocol:developfrom
waynercheung:fix/jsonrpc-tx-info-quantity-format-6547

Conversation

@waynercheung
Copy link
Copy Markdown
Collaborator

What this PR does

Fixes 9 spec-violation outputs in TransactionResult, the response body shared by eth_getTransactionByHash, eth_getTransactionByBlockHashAndIndex, eth_getTransactionByBlockNumberAndIndex, and the full-tx mode of eth_getBlockByHash / eth_getBlockByNumber.

All fixed cases share a single root cause: byte[N] zero-fill values were serialised via ByteArray.toJsonHex(byte[]), which preserves leading zero bytes, producing strings that violate the uint (QUANTITY) pattern. The fallback constructor additionally used a literal "0x" placeholder where the spec requires null or a valid uint.

Closes #6547.

Why

Per the Ethereum JSON-RPC schema in ethereum/execution-apis:

  • uint (QUANTITY) MUST match ^0x(0|[1-9a-f][0-9a-f]*)$ — no leading zeros; zero is 0x0.
  • TransactionInfo.blockHash / blockNumber / transactionIndex are nullable when block context is absent. "0x" matches none of these forms.

Strict Ethereum-compatible clients (geth ethclient, ethers.js, web3.js, Web3j) reject the malformed values produced today, so this PR also unblocks correct parsing on those clients.

What changes

Field Path Before After
nonce both constructors 0x0000000000000000 0x0
v parseSignature unsigned branch 0x00 0x0
r parseSignature unsigned branch 0x000…000 (64 zeros) 0x0
s parseSignature unsigned branch 0x000…000 (64 zeros) 0x0
blockHash fallback constructor "0x" null
blockNumber fallback constructor "0x" null
transactionIndex fallback constructor "0x" null
gasPrice fallback constructor "0x" 0x0

About the fallback constructor

TransactionResult(Transaction, Wallet) is invoked only on the data-pruning / inconsistency path inside TronJsonRpcImpl#getTransactionByHash: the transaction exists in TransactionStore (so it has been on-chain) but TransactionInfoStore / TransactionHistoryStore and BlockStore cannot serve the corresponding TransactionInfo or Block.

This is not the pending-pool path. java-tron's jsonrpc module does not consult the mempool (Manager#pendingTransactions); a transaction that lives only in the mempool already responds with null from eth_getTransactionByHash. Changing the fallback's malformed "0x" to spec-compliant null / 0x0 therefore does not affect mempool query behaviour — those queries continue to return null exactly as before.

Why Block.nonce is intentionally NOT changed

The original issue says nonce should be 0x0, but the spec types differ:

  • TransactionInfo.nonce is uint0x0000000000000000 violates it, must become 0x0. Fixed in this PR.
  • Block.nonce is bytes8, pattern ^0x[0-9a-f]{16}$ → the existing 0x0000000000000000 is already compliant. Changing it to 0x0 would shorten the value to 3 hex chars and break conformance.

BlockResult.nonce is therefore left untouched and the existing JsonrpcServiceTest assertions on it ("0x0000000000000000") remain valid.

Compatibility

  • Live networks: every TRON transaction in production carries a signature, so it goes through the signed path of parseSignature, which this PR does not touch. Production responses are byte-identical.
  • Fallback constructor path: triggered only by data pruning / inconsistency. The previous "0x" outputs were rejected by strict Ethereum clients (hexutil.UnmarshalJSON("0x") reports hex string "0x" is invalid); strict clients can now parse the response — null for unknown block context (matching RPCTransaction pointer-field semantics in geth), 0x0 for gasPrice. Lenient clients see no behavioural change.
  • Mempool queries: unaffected — eth_getTransactionByHash for a mempool-only transaction continues to return null (java-tron's RPC does not query the mempool).

Tests

TransactionResultTest is updated to:

  • Pin the new spec-compliant literal values (0x0 for nonce / v / r / s / gasPrice) and the new null values for the fallback path's block-context fields.
  • Validate every relevant QUANTITY field against the schema regex ^0x(0|[1-9a-f][0-9a-f]*)$ via a small assertQuantity helper, so any future regression that re-introduces a leading-zero or "0x" value will fail the test.

Pre-submit checklist

  • Single module (jsonrpc), single issue (The nonce returned by eth_getTransactionByHash is non-compliant #6547), 2 files changed
  • Code style follows Google Java Style
  • No debug code / temporary comments / stray TODOs
  • No unsafe numeric ops introduced
  • No log-level changes
  • No DB schema / consensus / config changes
  • No dependency upgrades
  • Comments explain WHY (the spec rationale), not WHAT

Per ethereum/execution-apis, uint (QUANTITY) fields must match the
pattern `^0x(0|[1-9a-f][0-9a-f]*)$` (no leading zeros), and contextual
fields are nullable when block context is absent. TransactionResult
violated this in several places.

What changes:
* nonce in both constructors emitted `0x0000000000000000` (8 zero
  bytes); per the TransactionInfo schema this field is uint, so it
  now emits `0x0`.
* In the unsigned-signature branch of parseSignature, v/r/s were
  built from `new byte[1]` / `new byte[32]`, producing `0x00` and 64
  zero hex chars; per the legacy signature schema these are uint, so
  they now emit `0x0`.
* In the fallback constructor (used when the containing block cannot
  be resolved), blockHash / blockNumber / transactionIndex were
  emitted as the literal `0x`, which is neither a valid hash32, nor a
  valid uint, nor null. Per spec they must be null when block context
  is unknown; gasPrice in the same path is corrected from `0x` to
  `0x0`.

Notes:
* Block.nonce is intentionally not changed -- the Block schema defines
  it as bytes8 (DATA), so `0x0000000000000000` is already compliant.
  Changing it would break conformance with the spec.
* The 32-byte r/s outputs in the signed path are not altered here; a
  conformance-correct rewrite has wider implications and will be
  tracked separately.

Strict Ethereum-compatible clients (geth ethclient, ethers.js,
web3.js, Web3j) reject malformed QUANTITY values, so this also
unblocks correct parsing on those clients.

Tests pin the spec-compliant values and validate each QUANTITY field
against the schema regex to prevent regressions.

Closes tronprotocol#6547
@waynercheung waynercheung changed the title fix(jsonrpc): correct zero-value QUANTITY/null format violations in TransactionResult fix(jsonrpc): correct QUANTITY/null format in TransactionResult Apr 27, 2026
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.

The nonce returned by eth_getTransactionByHash is non-compliant

1 participant