A comprehensive Bitcoin transaction and block analyzer with both CLI and web interfaces. Chain Lens parses raw Bitcoin transactions and blocks with protocol-level precision, generating detailed JSON reports while providing an accessible web UI that explains transactions to non-technical users through diagrams and plain language.
Perfect for developers building Bitcoin tools, analysts studying on-chain activity, and anyone wanting to deeply understand transaction structure and account flow on the Bitcoin network.
- Precise Transaction Parsing: Dissect raw Bitcoin transactions into structured, machine-verifiable JSON
- Block Analysis: Parse complete blocks with merkle root verification and undo file support
- Multiple Script Types: Full support for P2PKH, P2SH, P2WPKH, P2WSH, P2TR (keypath and scriptpath), and OP_RETURN
- SegWit Support: Complete SegWit and Taproot handling with witness discount analysis
- Timelock & RBF: BIP68 relative timelocks, absolute timelocks, and BIP125 RBF signaling detection
- Web Visualizer: Interactive transaction explorer with visual value flow diagrams and non-technical explanations
- Comprehensive Fee Analysis: Transaction weight, vbytes, and fee rate calculations following BIP141
- Block File Parsing: Support for Bitcoin Core's raw block files with XOR deobfuscation and undo records
- Robust Error Handling: Detailed error reporting with structured JSON responses
git clone https://github.com/yourusername/chain-lens.git
cd chain-lens
npm installAnalyze a single transaction:
./cli.sh fixtures/transactions/tx_legacy_p2pkh.jsonOutput is written to out/<txid>.json and printed to stdout for single transactions.
Parse a Bitcoin Core block file:
./cli.sh --block blk00000.dat rev00000.dat xor.datGenerates out/<block_hash>.json with complete block analysis.
Start the interactive transaction explorer:
./web.sh
# Opens at http://127.0.0.1:3000Upload a transaction fixture or paste JSON to analyze and visualize transactions with detailed breakdowns.
Chain Lens focuses on parsing and accounting, not cryptographic validation:
- Signatures and script execution are not validated (use Bitcoin Core for that)
- All required prevouts are assumed to be provided in the input
- No external network requests or node connectivity needed
src/
analyzer.js # Main transaction analysis engine
blockParser.js # Block-level parsing and validation
scriptParser.js # Bitcoin script disassembly and classification
txParser.js # Transaction serialization parsing
cli.js # CLI interface
web/ # Web visualizer
server.js # Express server
public/ # Frontend assets
fixtures/ # Test transaction and block data
grader/ # Test suite and grading scripts
# Single transaction mode
./cli.sh <fixture.json>
# Block mode
./cli.sh --block <blk*.dat> <rev*.dat> <xor.dat>Single-transaction mode:
- Reads fixture JSON with transaction and prevout data
- Writes JSON report to
out/<txid>.json - Prints report to stdout
- Returns exit code 0 on success, 1 on error
Block mode:
- Parses Bitcoin Core block and undo files with XOR deobfuscation
- Generates report for each block found in file
- Writes to
out/<block_hash>.json - Verifies merkle root matches computed value
- Extracts BIP34 block height from coinbase scriptSig
./web.sh
# Prints URL like: http://127.0.0.1:3000
# Runs until interrupted (Ctrl+C)Honors PORT environment variable (default 3000).
{
"network": "mainnet",
"raw_tx": "0200000001...",
"prevouts": [
{
"txid": "11...aa",
"vout": 0,
"value_sats": 123456,
"script_pubkey_hex": "0014..."
}
]
}Notes:
raw_txis hex-encoded (no0xprefix)prevoutsmay be in any order; matched to inputs by (txid, vout)- All spent outputs must be provided; missing prevouts cause errors
Chain Lens reads Bitcoin Core's native block format:
- blk.dat*: Block data file (may contain multiple blocks)
- rev.dat*: Undo file with prevout information for all non-coinbase inputs
- xor.dat: XOR obfuscation key (apply XOR to deobfuscate blk/rev files)
{
"ok": true,
"network": "mainnet",
"segwit": true,
"txid": "...",
"wtxid": "...",
"version": 2,
"locktime": 800000,
"size_bytes": 222,
"weight": 561,
"vbytes": 141,
"total_input_sats": 123456,
"total_output_sats": 120000,
"fee_sats": 3456,
"fee_rate_sat_vb": 24.51,
"rbf_signaling": true,
"locktime_type": "block_height",
"locktime_value": 800000,
"segwit_savings": {
"witness_bytes": 107,
"non_witness_bytes": 115,
"total_bytes": 222,
"weight_actual": 561,
"weight_if_legacy": 888,
"savings_pct": 36.82
},
"vin": [
{
"txid": "...",
"vout": 0,
"sequence": 4194311,
"script_sig_hex": "...",
"script_asm": "...",
"witness": ["..."],
"script_type": "p2wpkh",
"address": "bc1...",
"prevout": {
"value_sats": 123456,
"script_pubkey_hex": "..."
},
"relative_timelock": {
"enabled": true,
"type": "blocks",
"value": 7
}
}
],
"vout": [
{
"n": 0,
"value_sats": 120000,
"script_pubkey_hex": "...",
"script_asm": "OP_0 OP_PUSHBYTES_20 89abcdef0123456789abcdef0123456789abcdef",
"script_type": "p2wpkh",
"address": "bc1..."
},
{
"n": 1,
"value_sats": 0,
"script_pubkey_hex": "6a08736f622d32303236",
"script_asm": "OP_RETURN OP_PUSHBYTES_8 736f622d32303236",
"script_type": "op_return",
"address": null,
"op_return_data_hex": "736f622d32303236",
"op_return_data_utf8": "sob-2026",
"op_return_protocol": "unknown"
}
],
"warnings": [
{ "code": "RBF_SIGNALING" }
]
}{
"ok": true,
"mode": "block",
"block_header": {
"version": 536870912,
"prev_block_hash": "...",
"merkle_root": "...",
"merkle_root_valid": true,
"timestamp": 1710000000,
"bits": "...",
"nonce": 12345,
"block_hash": "..."
},
"tx_count": 150,
"coinbase": {
"bip34_height": 800000,
"coinbase_script_hex": "...",
"total_output_sats": 631250000
},
"transactions": ["/* transaction objects */"],
"block_stats": {
"total_fees_sats": 6250000,
"total_weight": 3996000,
"avg_fee_rate_sat_vb": 25.1,
"script_type_summary": {
"p2wpkh": 420,
"p2tr": 180,
"p2sh": 55,
"p2pkh": 30,
"p2wsh": 12,
"op_return": 8,
"unknown": 2
}
}
}{ "ok": false, "error": { "code": "INVALID_TX", "message": "..." } }- p2pkh (Pay to Public Key Hash)
- p2sh (Pay to Script Hash)
- p2wpkh (Pay to Witness Public Key Hash)
- p2wsh (Pay to Witness Script Hash)
- p2tr (Pay to Taproot)
- op_return (OP_RETURN data carrier)
- unknown (unrecognized script pattern)
- p2pkh (legacy Pay to Public Key Hash)
- p2sh-p2wpkh (nested SegWit)
- p2sh-p2wsh (nested SegWit multisig)
- p2wpkh (native SegWit v0)
- p2wsh (native SegWit v0 script hash)
- p2tr_keypath (Taproot keypath spend)
- p2tr_scriptpath (Taproot scriptpath spend)
- unknown (unrecognized or invalid)
Each output and input includes:
- script_type: Classification from supported types (see above)
- script_asm: Human-readable disassembly with opcodes and data
- address: Standard address format for recognized types,
nullotherwise
Opcode rendering:
- Standard opcodes:
OP_DUP,OP_HASH160,OP_CHECKSIG, etc. - Data pushes:
OP_PUSHBYTES_<n> <hex>for direct pushes,OP_PUSHDATA1/2/4 <hex>for extended pushes - Values:
OP_0throughOP_16rendered explicitly
Example: OP_DUP OP_HASH160 OP_PUSHBYTES_20 89abcdef... OP_EQUALVERIFY OP_CHECKSIG
For P2WSH inputs, the last witness item (witnessScript) includes witness_script_asm.
For OP_RETURN outputs, additionally provides:
- op_return_data_hex: Concatenation of all data pushes after OP_RETURN
- op_return_data_utf8: UTF-8 decoded string (or
nullif not valid UTF-8) - op_return_protocol: Detected protocol prefix (
"omni","opentimestamps", or"unknown")
Supports all valid push opcodes (single push or multiple pushes concatenated).
- witness: Array of witness stack items for SegWit inputs, empty array for legacy
- wtxid: Transaction ID excluding witness data (null for non-SegWit)
- segwit_savings: Only present for SegWit transactions, showing:
- Witness vs non-witness byte counts
- Hypothetical legacy weight for comparison
- Percentage savings from SegWit discount
- locktime_type:
"none","block_height", or"unix_timestamp" - rbf_signaling:
trueif any input signals BIP125 replaceability - relative_timelock (per input): Block-based or time-based (BIP68), with
enabledflag
Computed according to BIP141:
- size_bytes: Total transaction size
- weight: BIP141 weight units (4 × non-witness + witness bytes)
- vbytes: Virtual bytes (weight / 4)
- fee_rate_sat_vb: Satoshis per vByte
Emitted when:
- HIGH_FEE:
fee_sats > 1,000,000ORfee_rate_sat_vb > 200 - DUST_OUTPUT: Any non-OP_RETURN output with
value < 546sats - UNKNOWN_OUTPUT_SCRIPT: Any output with unrecognized script type
- RBF_SIGNALING: Transaction signals BIP125 replaceability
Your web app provides an interactive analysis and visualizer for Bitcoin transactions, designed to be understandable to non-technical audiences.
- Transaction Loading: Paste JSON fixture or raw transaction hex with prevouts
- Block Analysis: Upload Bitcoin Core block files for detailed block-level analysis
- Visual Flow Diagram: Input-to-output value flow with clear connection lines
- Plain-Language Breakdown: Explain transaction structure without jargon (or define terms inline)
- Technical Details Toggle: Switch between simplified and detailed hex/script views
- Fee Visualization: Show fee as "missing value" between inputs and outputs
- Responsive Design: Works on desktop and mobile browsers
- No External Dependencies: Fully functional offline after installation
- GET /api/health — Health check; returns
{ "ok": true } - POST /api/analyze — Analyze transaction; request body:
{ "raw_tx": "...", "prevouts": [...] }
Public test fixtures are included in fixtures/:
./cli.sh fixtures/transactions/tx_legacy_p2pkh.json
./cli.sh fixtures/transactions/tx_segwit_p2wpkh_p2tr.json
./cli.sh fixtures/transactions/multi_input_segwit.jsonRun all tests with the grading script:
./grader/grade_transactions.shFor block mode testing, Bitcoin Core block files can be used with real mainnet data.
CLI (Node.js)
src/txParser.js— Serialization and transaction parsingsrc/scriptParser.js— Bitcoin script classification and disassemblysrc/analyzer.js— Main analysis engine (fees, timelocks, warnings)src/blockParser.js— Block header, merkle verification, undo file parsingsrc/cli.js— CLI entry point
Web (Node.js + vanilla frontend)
src/web/server.js— Express server with JSON APIsrc/web/public/index.html— Interactive visualizer interface- Built-in transaction and block analysis via
/api/analyze
- Node.js 14+
- No external library dependencies required for core parsing
npm install
./cli.sh fixtures/transactions/tx_legacy_p2pkh.json
./web.sh
# Navigate to http://127.0.0.1:3000Create a JSON fixture:
{
"network": "mainnet",
"raw_tx": "0200000001...",
"prevouts": [
{
"txid": "...",
"vout": 0,
"value_sats": 50000,
"script_pubkey_hex": "..."
}
]
}Analyze:
./cli.sh your_fixture.jsonThis implementation follows Bitcoin protocol specifications:
- BIP141: Segregated Witness — Transaction weight and vbyte calculation
- BIP68: Relative Lock-time — Sequence-based timelocks
- BIP125: Transaction Replacement — RBF signaling
- BIP34: Block v2 — Coinbase height encoding
- Bitcoin Core transaction serialization format
- Bitcoin Core block file format (
blk*.dat,rev*.dat, XOR obfuscation)
Contributions are welcome! Please:
- Follow existing code style
- Test changes with provided fixtures
- Add test cases for new functionality
- Submit a pull request with a clear description
For questions, bugs, or feature requests, please open an issue on GitHub.