Hackathon
Chain Lens
A Bitcoin transaction and block analyzer that parses raw Core files, reconstructs accounting and identifies supported script types, with a CLI and 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 scripts into supported types or an explicit unknown category, 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. Supported output types include P2PKH, P2SH, P2WPKH,
P2WSH, P2TR and OP_RETURN; address-bearing scripts are rendered as mainnet addresses. Inputs are 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 interpreting the format on this code. Checking the Merkle root compares the parsed transaction identifiers against the commitment in the header; it does not validate every decoded field or the block’s consensus rules.
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.