Skip to content

morph_getTransactionHashesByReference: result order and empty result differ from morph-geth #208

Description

@kriss39

Summary

morph_getTransactionHashesByReference returns a different order (and a different empty result) on morph-reth than on morph-geth. Both differences come from the index key layout.

1. Result order differs when consecutive blocks share a timestamp

morph-geth keys the reference index by reference || blockTimestamp || txIndex || txHash (core/rawdb/schema.go referenceIndexKey) and pages by iterating that key space, so results are ordered by (blockTimestamp, txIndex, txHash).

morph-reth keys ReferenceIndex by reference || block_number || transaction_index || transaction_hash (crates/reference-index/src/tables.rs), so results are ordered by (blockNumber, txIndex, txHash).

Since Emerald a block may carry the same timestamp as its parent, and with the default 300 ms fast block interval this is routine: in a 200-block window ending at mainnet block 26701490, 12 consecutive pairs shared a timestamp (e.g. 26701309/26701310 at 1789278633, 26701318/26701319 at 1789278648).

For a reference that appears in two such blocks, say block N at index 5 and block N+1 at index 2, morph-geth returns the block N+1 entry before the block N entry, while morph-reth returns them in chain order. Because pagination is by key order, offset/limit windows also cut at different places on the two clients, so a client paging across nodes of both kinds can see an entry twice or miss one.

The documentation (about-morph/11-morphtx.md) describes the morph-geth order ("sorted by block timestamp + tx index, ascending"), but that order is not chronological within an equal-timestamp run; the morph-reth order is. Fixing this on the morph-geth side means changing the key layout (block number instead of, or in front of, the timestamp) and re-indexing existing databases; fixing it on the morph-reth side means replicating the interleaving. Either way the two clients should agree, so I wanted to raise it here before touching either.

2. Empty result: null vs []

morph-geth returns nil, nil when the page is empty (internal/ethapi/api.go, PublicMorphAPI.GetTransactionHashesByReference), which serializes as "result": null. morph-reth returns [] (both from query_at and from the explicit pre-Jade short-circuit in crates/rpc/src/morph/handler.rs).

morph-geth is not fully consistent with itself either: if index entries exist but every ReadTxLookupEntry misses, it returns a non-nil empty slice, i.e. [].

Notes

Happy to implement whichever direction you prefer for either point (a block-number-first key on the geth side with a reindex, or matching null on the reth side).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions