Making the transaction, its receipt and its block agree first
Forensia now refuses to interpret an Ethereum-style transaction unless three captured records agree on what it is and where it sits. This area is still being extended, and it is not a proof of inclusion or finality.
To read a transaction, a tool asks a data provider several separate questions: what was sent, what happened when it ran, and what block it landed in. The answers come back as separate records. Usually they describe the same event. If they do not, because the chain reorganized between requests or a response is simply wrong, everything built on top is unreliable.
Forensia now checks that those records agree about the transaction and its exact position in the block before it explains anything. If they disagree, the lookup stops. The agreed facts are kept as a context record you can trace back to its three sources.
This area is still being extended. The position check and the format context described here are in place. The explanation of the rules a historical transaction ran under is partial: Ethereum mainnet only, a lookup against a pinned reference and not a replay, with the open parts listed at the end.
#Plain languageThree documents that should describe one parcel
Picture a parcel with three pieces of paperwork. The shipping label says what was sent and by whom. The delivery slip says whether it arrived and what happened. The truck manifest lists every parcel in loading order.
If the label says truck 12, position 4, then the delivery slip should say the same, and the manifest for truck 12 should show this parcel at position 4, once, and nowhere else. Forensia makes that comparison for the transaction (the label), the receipt (the delivery slip) and the block (the manifest).
Agreement between the three papers tells you the paperwork is consistent. It does not tell you the courier company is honest. All three records come from the same provider.
#How it worksWhere the result of the check shows up
You do not run this check. It happens during every transaction lookup on an EVM network. If it fails, you see an error saying the transaction, receipt and captured block do not agree, with a suggestion to retry, and no activity or flow is shown.
When it passes, the agreed context is available on demand:
- Look up a transaction and stay on Overview.
- Open Inspect transaction steps.
- Open the section named Network fees and transaction context.
- Read the rows: Network, Network in transaction, Block (with position and zero-based index), Block hash, Transaction format, Receipt format, Trace format, Historical fork rules and Context decoder.
- Use the three buttons to open the exact source record behind each part: transaction format source, receipt format source and block position source.
For example, the Ethereum transaction 0x698dc47038978fad61d69a87d14b98679e37a772fa06d726523778d1e34530a7 shows type 2 (dynamic fee) for both the transaction and the receipt, chain 1, block 15314525, position 1 (index 0), and a fork label of Gray Glacier marked as a reference schedule match, not replayed.
To check the saved copy later, go to Sources, open Capture integrity & local evidence, and choose Check local evidence. A block headed Execution context reports matched, needs review or incomplete. That local check is not available on the public beta, which does not retain raw response bodies.
#Technical detailThe position rule, the format context and the fee arithmetic
Three requests
| JSON-RPC method | Parameters | Fields used |
|---|---|---|
| eth_getTransactionByHash | [hash] | hash, blockHash, blockNumber, transactionIndex, type, chainId, v, gas |
| eth_getTransactionReceipt | [hash] | transactionHash, blockHash, blockNumber, transactionIndex, type, status, gasUsed, effectiveGasPrice, blobGasUsed, blobGasPrice |
| eth_getBlockByNumber | [block number from the transaction, false] | hash, number, timestamp, difficulty, transactions (hash list) |
The position rule
The function returns a zero-based index or null. Null stops the lookup. It returns an index only when all of the following hold:
- The transaction hash and the receipt’s transactionHash both equal the requested hash.
- The transaction and the receipt each carry a blockHash equal to the block’s hash and a blockNumber equal to the block’s number.
- Each carries a transactionIndex that is a canonical hex quantity and is smaller than the length of the block’s transaction list. A missing or null index is not treated as zero.
- The two indexes are equal.
- Every entry in the block list is a valid 32-byte hash, and no hash appears twice (compared without regard to letter case).
- The requested hash sits at the declared index and at no other index.
- If the block returned full transaction objects, any transactionIndex, blockHash or blockNumber an entry declares must match its slot and the block.
- The list is not empty and has at most 100,000 entries.
The check makes no extra request, and the index it produces is reused by the optional state capture.
The context record
decoder: "forensia.execution-context.v2"
chainId, transactionHash, blockHash, blockNumber, transactionIndex
transactionType, receiptType // null when the source did not supply one
reportedChainId, signatureV
blockTimestamp, blockDifficulty
forkSchedule, historicalFork
clientVersion: "unknown"
transactionEvidenceId, receiptEvidenceId, blockEvidenceIdTyped transactions follow EIP-2718, so the transaction type and the receipt type must be equal when both are present. A missing type stays unknown. It is never read as legacy type 0. On Ethereum mainnet, types 0 to 4 get a label (legacy, access list, dynamic fee, blob, set-code authorization). On other networks the number is kept without a label.
The chain ID inside the transaction must equal the network you looked up. One exception follows EIP-155: a type 0 transaction on mainnet with v equal to 27 or 28 may report no chain ID or zero. It is shown as having no signed chain binding, and the signature is not verified. For type 4, the panel adds that EIP-7702 authorization processing is separate from call execution and that the sender is not necessarily an authorization signer.
The fork label
The label comes from a pinned schedule, forensia.ethereum-mainnet-schedule.v1, checked against one fixed go-ethereum configuration commit. Eras up to Gray Glacier are chosen by block number. From block 15,537,394 onward the block must also report zero difficulty and a timestamp at or after the Paris anchor, and later eras (Shanghai, Cancun, Prague, Osaka) are chosen by timestamp. A block whose number, time and difficulty disagree about the merge is a conflict. So is a known typed format that appears before its activation: type 1 before block 12,244,000, type 2 before block 12,965,000, type 3 before Cancun, type 4 before Prague.
Blocks timestamped on or after 2026-10-07 00:00 UTC get no label under this schedule version. The cutoff is fixed and does not roll forward. Any network other than chain 1 is reported as unknown.
Call and permission explanations depend on this. They are withheld unless the capture is mainnet, has a matched reference, has equal types in the range 0 to 4, and includes a call tree. A DELEGATECALL frame before block 1,150,000, a STATICCALL frame before 4,370,000 or a CREATE2 frame before 7,280,000 gets no sender or value context on that branch.
Fees from the same records
The network fee is exact integer arithmetic: receipt gasUsed times effectiveGasPrice. For type 3, blobGasUsed times blobGasPrice is added and shown separately. The gas limit, cumulative gas and fee caps are never substituted. Gas used above the gas limit, blob gas that is not a multiple of 131,072, or blob fields on a non-blob transaction give no figure. In testing before release, the blob transaction 0x5b0837bfd469cb20f5474de25bb4270934133831fca64196e7845b33acdaead5 produced an execution fee of 280656373253215 wei, a blob fee of 393216 wei and a total of 280656373646431 wei.
The local check takes the three hash-verified bodies, each up to 8 MiB, compares these same fields, then rebuilds the context from scratch. An available contradiction outranks a missing body. Fewer than three bodies is reported as incomplete.
#LimitsWhat agreement between three records does not give you
- The fork label is a match against a pinned reference table. Nothing was replayed under those rules.
- The software that served the trace is unknown. The words Geth-compatible callTracer describe a response format, not a node or its version.
- Other networks have no pinned schedule yet, so they get no fork label and no Ethereum fee rules. Other networks can have additional fees.
- Signatures are not verified, and whether an EIP-7702 authorization was applied is not determined.
- The fee is arithmetic on captured fields. It is not the sender’s full balance change, not a payment to a contract, and it can be charged when execution fails.
- The check establishes one transaction’s slot. It does not order calls against receipt logs inside that transaction.
- The Paris check uses reported difficulty and fixed anchors. It does not verify total difficulty or ancestor blocks.
- Existing saved captures are not rewritten, enriched or invalidated by the newer lookup check.
- Still open: schedules for other networks, the identity of the historical backend and tracer, canonical-chain and finality checks, correct historical pre-state, and replay.