Checking what the screen says against the responses that were kept
Forensia rebuilds its supported readings from the provider responses retained in your browser and blocks saving and export on a contradiction. It checks consistency with those bytes. It does not re-execute the chain.
An investigation tool shows you a reading of data: this call moved that amount, this approval came before that transfer. The reading was produced from responses a data provider sent at one moment. If the reading and the stored responses ever drift apart, through a bug, an edited file or damaged storage, you want to know before the result goes in a report.
Forensia now does that check on request. It takes the provider responses kept in your browser, confirms they are the same bytes that were captured, rebuilds each supported interpretation from them, and compares the rebuilt version with what is on screen. If they disagree, Report, saving the result and the combined raw export are blocked.
This is a consistency check between an interpretation and its own source material. It is not an independent execution of the blockchain, and it does not test whether the provider told the truth.
#Plain languageChecking the summary against the photocopy
Suppose you wrote a summary of a bank statement, and you kept a photocopy of the statement in a sealed envelope. Later you confirm the seal is intact and read the photocopy line by line against your summary. If your summary says 400 and the photocopy says 300, something is wrong with the summary, and you should not send it to anyone.
Note what this cannot tell you. If the bank printed a wrong statement, the photocopy is wrong in the same way, and your summary will match it perfectly. A match means the summary is faithful to the copy you kept. Nothing more.
#How it worksRunning the check from Sources
- Open a result and go to Sources.
- Open the section named Capture integrity & local evidence.
- Under What can you verify now, choose Check local evidence. The check reads only this browser’s vault and contacts no provider.
- Read the first line (how many response bodies were verified locally), then the result blocks below it. Each block covers one kind of interpretation and appears only when the result contains that kind.
| Block | Matched heading | Conflict heading |
|---|---|---|
| Token steps | Token steps checked against saved receipts | Token interpretation needs review |
| Account changes | Account changes match saved responses | Account changes need review |
| Historical code | Historical code matches saved responses | Historical code needs review |
| Contract calls | Contract calls match saved responses | Contract calls need review |
| Permission events | Permission events match saved receipt | Permission events need review |
| Wrapping action | Wrapping action matches saved responses | Wrapping action needs review |
| Execution context | Execution context matches saved responses | Execution context needs review |
| Bitcoin allocation | Bitcoin inputs and outputs match saved response | Bitcoin allocation needs review |
Anything else is shown as incomplete, with the reason: a body was never saved, the local copy expired, or a size limit was reached. Incomplete means unchecked. It does not mean disproven, and it does not count as a match.
On a conflict, a banner titled Interpretation conflicts with saved evidence appears, any report link already prepared is withdrawn, and the Report button, Save result and the combined raw export stay disabled for that result.
#Technical detailTwo stages: verify the bytes, then rebuild the reading
Stage one: the bytes
Response bodies are stored in IndexedDB as gzip-compressed, AES-GCM encrypted blobs keyed by the SHA-256 of the original response, with a 30-day retention period. For each source record the verifier requires exactly one link and exactly one blob, a matching manifest digest, a matching response digest, a matching media type and a matching original byte length. It decrypts with the digests as additional authenticated data, checks the compressed size and compressed SHA-256, decompresses with a running cap so an oversized stream is cancelled, then checks the original size and original SHA-256.
| Limit | Value |
|---|---|
| Original bytes per response | 8 MiB |
| Compressed bytes per response | 1,000,000 |
| Total original bytes per check | 64 MiB |
| Records per check | 512 |
Only bodies that pass are handed to stage two. Each record ends in one state: verified, reference only, no response captured, body not saved, missing, expired, failed, key unavailable, or check limit reached.
Stage two: the interpretations
Each interpreter receives a bounded number of verified bodies and re-runs the same decoder that produced the display. Every one first checks the JSON-RPC envelope: version 2.0, a response id equal to the request id, no error member, and strict UTF-8.
| Check | Verified bodies used | What is compared |
|---|---|---|
| Token steps | Each eth_getTransactionReceipt body; up to 256 steps and 16,384 logs | Kind, sender, recipient, token contract, raw value, token ID, batch index, execution status, and the linked activity row, keyed by log index |
| Account changes | Up to 6 state and code responses | Account-change view and transaction-start code, in canonical JSON so key order is ignored |
| Historical code | Up to 4 code responses, 128 KiB each | Requested address, containing-block request and returned bytes, compared without regard to hex letter case |
| Contract calls | Exactly 3: eth_getTransactionByHash, eth_getTransactionReceipt, debug_traceTransaction with the callTracer | Root sender, target, value, selector and status; then every nested call’s path, order, depth, call type, sender, target, value, selector, status and failure origin |
| Permission events | 1 receipt; up to 48 events and 24 sequences, 4,096 decoded transfers | Event identity, participants, emitting contract, raw quantity or token ID, log position, grant or revocation state, and each approval-then-outflow sequence |
| Wrapping action | 3: transaction, receipt, block | Account, input, exact amount, event position, successful receipt, and the transaction appearing exactly once in the block list |
| Execution context | 3: transaction, receipt, block | Transaction and receipt type, reported chain ID, signature v, block hash, height, index, and recorded fee inputs |
| Bitcoin allocation | 1 transaction response, 1 MiB | Ordered inputs and outputs, participants, integer satoshi amounts, fee, counts and block |
The call check rebuilds the call tree at the limits recorded in the capture, with hard ceilings of 256 calls and depth 12. Call types such as CALL, DELEGATECALL and STATICCALL are compared as the tracer returned them, and a retained transferFrom call has its argument and return bytes compared too. Activity rows for internal calls must each map to one retained call ordinal. Activity may show fewer calls than were retained, never extra ones.
Rules that apply to every check
- A contradiction outranks a gap. If one body is missing and another available body disagrees with the display, the result is a conflict, not incomplete.
- A full match needs every required body. Two of three is reported as incomplete.
- Nothing is fetched, repaired or refreshed. The capture time, digests and stored bytes are never rewritten.
- Captures made after decoder versioning was added record which decoder produced them, for example forensia.geth-call-tree.v1 and forensia.receipt-transfers.v1. An unknown or contradictory version is a conflict before any comparison runs.
- Older captures without call paths or version records are left as unchecked. They are not reinterpreted under newer rules.
- The combined raw export runs the full verification again on its own and refuses on any conflict.
In local review, the Ethereum transaction 0x22fc4a1bba0e2a711a5852f5c835eea4cb764f989895b1b48ec73d84dab8ed95 checked with 31 of 31 body hashes, 30 token items, five state responses, four code observations and 64 nested calls matched. A second, 0x698dc47038978fad61d69a87d14b98679e37a772fa06d726523778d1e34530a7, matched four permission events and three sequences. These are selected examples, not a benchmark.
#LimitsWhat a match does not mean
- A matching hash shows the stored bytes are the captured bytes. It does not authenticate the provider or its content.
- No receipt-trie proof, consensus check or finality check is performed.
- The call check does not establish which code actually ran, that an allowance was consumed, or that a given call caused a given event.
- The code check compares a provider’s answer for the containing block. It does not identify the code at the moment of the call or resolve an EIP-7702 delegation target.
- The permission check covers events inside one receipt. It does not cover current allowance, permit signatures, token compliance or wallet-history windows.
- The wrapping check covers direct calls to the Ethereum mainnet WETH contract only. Routers, swaps and other deployments are not relabeled as supported.
- Only retained items are checked. Matching a bounded sample never means the trace or the receipt was complete.
- Findings outside these eight checks are not verified by them.
- Files you downloaded before a conflict was found cannot be recalled.
- Clearing browser data or reaching the retention expiry removes the bodies. A hash cannot bring them back.