Hackathon
Chain Lens
A Bitcoin transaction and block analyzer that parses raw Core data files, reconstructs the accounting, and classifies every script, with a CLI and a visual walkthrough sharing one engine.
Chain Lens takes raw Bitcoin data and produces a report you can check rather than a summary you have to believe. It parses transactions and whole blocks, rebuilds inputs, outputs and fees, classifies every script, and emits either structured JSON or an interactive walkthrough.
- Context
- Timed developer challenge, with an automated grader
- Surfaces
- CLI (JSON) and web visualiser
- Inputs
- Raw transactions and Bitcoin Core blk*.dat / rev*.dat
- Role
- Author
- Repository
- VincenzoImp/2026-developer-challenge-1-chain-lens
The problem
A block explorer tells you what a transaction did. It does not tell you how it arrived at that answer, and for anything involving fees, weight or script types the answer depends on rules that changed several times: SegWit’s weight accounting, Taproot’s spend paths, the way a witness discount alters what a transaction actually costs.
The challenge was to compute those from the bytes and be checkable about it, which rules out calling an API and reformatting the reply.
What I built
A single analysis engine with two front ends over it.
For a transaction it derives txid and wtxid, size, weight and vbytes under BIP141, input and
output totals, the fee and the fee rate. Every output script is classified – P2PKH, P2SH, P2WPKH,
P2WSH, P2TR, OP_RETURN – and rendered as its mainnet address; every input is classified by how
it was spent, down to distinguishing a Taproot key-path spend from a script-path one. It
disassembles scripts, decodes OP_RETURN payloads and recognises Omni and OpenTimestamps inside
them, reports BIP125 replaceability and BIP68 relative timelocks, and compares actual weight
against the hypothetical legacy weight to show what SegWit saved.
In block mode it reads Bitcoin Core’s own blk*.dat and rev*.dat files – de-obfuscating them
with xor.dat first – verifies the merkle root against the block header, decodes the BIP34
height from the coinbase, and aggregates fees, weight and script-type distribution across the
block.
Hard parts
One engine, two surfaces, or the numbers diverge. The CLI and the visualiser both call
lib/analyzer.ts, and the repository states the reason plainly: so that the two always produce
the same numbers. A visualiser with its own fee calculation is a second implementation of
consensus-adjacent arithmetic, and the moment the two disagree neither can be trusted.
Reading Core’s files rather than its RPC. Block files are an internal format, obfuscated on disk and not a stable interface. Parsing them directly removes the dependency on a running node and puts the burden of getting the format right entirely on this code, which is why verifying the merkle root matters: it is the check that the parse was correct, computed from the parsed data against the header.
Being gradeable. The repository carries a grader with expected outputs. Emitting structured
JSON rather than prose is what makes the analysis machine-checkable, and it is why the
classification vocabulary has to be exhaustive rather than approximate: unknown is a real
category and has to be returned honestly.