Skip to content

Latest commit

 

History

212 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BitIodine

CI License: MIT/Apache-2.0 Rust: 1.75+

A high-performance, zero-copy Bitcoin blockchain parser and address clusterizer written in Rust.

BitIodine reads raw blk*.dat block files produced by bitcoind via memory mapping (mmap), parses blocks, headers, transactions, and script bytecodes, and provides an extensible Visitor pattern to analyze blockchain data, extract address balances, find OP_RETURN data outputs, and cluster co-spent addresses using Tarjan's Union-Find algorithm.


Features & Modern bitcoind Compatibility

BitIodine is fully compatible with block files generated by all modern versions of Bitcoin Core (bitcoind v0.13 through v28.x+).

Supported Script & Address Formats

Script Type Standard Address Prefix Status
Pay-to-Pubkey (P2PK) Legacy (Genesis) Raw compressed / uncompressed pubkey ✅ Supported
Pay-to-PubkeyHash (P2PKH) Legacy 1... (Base58) ✅ Supported
Pay-to-ScriptHash (P2SH) BIP-16 3... (Base58) ✅ Supported
P2SH-wrapped SegWit (P2SH-P2WPKH / P2SH-P2WSH) BIP-141 / BIP-143 3... (Base58) ✅ Supported
Native SegWit v0 (P2WPKH) BIP-141 / BIP-173 bc1q... (Bech32) ✅ Supported
Native SegWit v0 (P2WSH) BIP-141 / BIP-173 bc1q... (Bech32) ✅ Supported
Taproot / SegWit v1 (P2TR) BIP-341 / BIP-350 bc1p... (Bech32m) ✅ Supported
Future SegWit Witness Programs (v2–v16) BIP-141 / BIP-350 bc1... (Bech32m) ✅ Supported
Bare Multisig (OP_CHECKMULTISIG) Legacy / BIP-11 M-of-N public keys ✅ Supported
Null Data (OP_RETURN) Core 0.9+ Arbitrary payload data ✅ Supported
Relative Timelocks (OP_CSV) BIP-112 OP_CHECKSEQUENCEVERIFY ✅ Supported
Absolute Timelocks (OP_CLTV) BIP-65 OP_CHECKLOCKTIMEVERIFY ✅ Supported

Core Parser Capabilities

  • Zero-Copy Memory-Mapped Parsing: Maps blk*.dat files directly into virtual memory for blazing-fast sequential scanning.
  • 👥 Address Clustering with CoinJoin Protection: Multi-input heuristic clustering with rank-based Union-Find (DisjointSet), iterative path compression, and heuristic filtering for equal-denomination CoinJoin rounds.
  • 🧩 Pluggable Visitor Architecture: Clean BlockChainVisitor trait to write custom analyzers, heuristics, and indexers.
  • 🛡️ Pure-Rust Cryptography: Uses standard RustCrypto ecosystem crates (sha2, ripemd, bs58, hex) without legacy C dependencies.
  • 🖥️ CLI with Dynamic Action Selection: Run any visitor directly from the command line without recompiling.

Node Requirements & Prerequisites

To parse the blockchain with BitIodine, point the parser to an active or completed Bitcoin Core data directory:

  1. Unpruned Node Required:
    • bitcoind must be running with prune=0 (the default full-node setting).
    • Pruned nodes delete early blk*.dat files (blk00000.dat onwards), preventing full historical analysis from Genesis.
  2. Default Block Paths:
    • Linux: ~/.bitcoin/blocks/ (or /var/lib/bitcoind/blocks/)
    • macOS: ~/Library/Application Support/Bitcoin/blocks/
    • Windows: %APPDATA%\Bitcoin\blocks\

Building

Requires a stable Rust toolchain (1.75 or later).

cargo build --release

The optimized executable will be located at target/release/bitiodine.


Usage

A high-performance Bitcoin blockchain parser and address clusterizer in Rust.

Usage: bitiodine [OPTIONS]

Options:
  -b, --blocks-dir <BLOCKS_DIR>  Path to the bitcoind blocks directory [default: ~/.bitcoin/blocks]
  -o, --output <OUTPUT>          Path to the output file [default: clusters.csv]
  -a, --action <ACTION>          Action / visitor to run [default: clusterizer]
                                 [possible values: clusterizer, dump-balances, dump-addresses,
                                  dump-tx-hashes, dataoutput-finder, donation-finder, merkle]
  -v...                          Sets the level of verbosity (-v for debug, -vv for trace)
  -h, --help                     Print help
  -V, --version                  Print version

Available Actions

Action Description Output
clusterizer (default) Groups together co-spent addresses into ownership clusters CSV (<address>,<cluster_id>)
dump-balances Calculates unspent balances per address CSV (<balance_btc>,<hash160>,<address>)
dump-addresses Extracts all unique recipient addresses across transactions Stdout
dump-tx-hashes Logs transaction IDs at regular block intervals Stdout
dataoutput-finder Discovers and prints OP_RETURN arbitrary payload messages Stdout
donation-finder Identifies potential donation or non-standard outputs Stdout
merkle Computes and validates every block's Merkle root against its header Stdout / Logs

Examples

Run address clusterizer against default bitcoin directory:

./target/release/bitiodine -o clusters.csv

Dump address balances from a custom blocks path with debug logging:

./target/release/bitiodine -b /var/lib/bitcoind/blocks -a dump-balances -o balances.csv -v

Find OP_RETURN data payloads:

./target/release/bitiodine -b /var/lib/bitcoind/blocks -a dataoutput-finder

Architecture

BitIodine is structured as a library (bitiodine) and a CLI application.

High-Level System Architecture

flowchart TD
    subgraph Storage["1. Bitcoin Core Storage"]
        BLK["blk*.dat Block Files<br/>128 MB Raw Disk Chunks"]
    end

    subgraph Core["2. BitIodine Zero-Copy Engine"]
        MMAP["memmap2 Memory Mapping"]
        SEQ["Block Sequencing & Orphan Reassembly<br/>In-memory skipped blocks table"]
        FRAMER["Block & Header Framing<br/>80-byte header, Merkle root, timestamp"]
        TXP["Transaction & Witness Deserializer<br/>txid and wtxid computation"]
        SCRIPT["Script & Bytecode Engine<br/>P2PK, P2PKH, P2SH, SegWit v0-v16, Taproot, OP_RETURN"]
        UTXO["OutputMap UTXO Tracker<br/>HashMap of Unspent Output Items"]
    end

    subgraph Visitors["3. Pluggable Visitor Engine (BlockChainVisitor)"]
        CLUST["Clusterizer<br/>Union-Find DisjointSet with CoinJoin Filter"]
        BAL["DumpBalances<br/>Balance Accounting Engine"]
        ADDR["DumpAddresses<br/>Address Extractor"]
        MERK["MerkleVisitor<br/>Header Merkle Root Verifier"]
        DATA["DataOutputFinder<br/>OP_RETURN Payload Extractor"]
    end

    subgraph Sinks["4. Output Sinks"]
        CSV["CSV Files<br/>clusters.csv / balances.csv"]
        STDOUT["Stdout Stream"]
        LOGS["Logger & Metrics"]
    end

    BLK --> MMAP
    MMAP --> SEQ
    SEQ --> FRAMER
    FRAMER --> TXP
    TXP --> SCRIPT
    TXP <--> UTXO
    TXP --> Visitors

    CLUST --> CSV
    BAL --> CSV
    ADDR --> STDOUT
    MERK --> LOGS
    DATA --> STDOUT
Loading

Visitor Event Lifecycle & Traversal Flow

sequenceDiagram
    autonumber
    participant BC as BlockChain Engine
    participant V as BlockChainVisitor
    participant UTXO as OutputMap (UTXO State)

    Note over BC,V: For each block in sequential height order
    BC->>V: visit_block_begin(block, height)
    Note over BC,V: For each transaction in block
    BC->>V: visit_transaction_begin(block_item)

    loop For each Transaction Input
        BC->>UTXO: Lookup & consume spent output (prev_hash, prev_index)
        UTXO-->>BC: Some(spent_output_item)
        BC->>V: visit_transaction_input(txin, block_item, tx_item, spent_output_item)
    end

    loop For each Transaction Output
        BC->>V: visit_transaction_output(txout, block_item, tx_item)
        V-->>BC: Option<OutputItem> (e.g. Address, Value)
        BC->>UTXO: Store new unspent output item
    end

    BC->>V: visit_transaction_end(tx, block_item, tx_item)
    BC->>V: visit_block_end(block, height, block_item)

    Note over BC,V: When all block files have been processed
    BC->>V: done()
Loading

Address Clustering Pipeline (Clusterizer)

flowchart LR
    subgraph Inputs["Multi-Input Transaction"]
        IN1["Address A"]
        IN2["Address B"]
        IN3["Address C"]
    end

    subgraph Filter["Heuristic Filters"]
        CJ{"CoinJoin Check<br/>≥3 equal output amounts?"}
    end

    subgraph UnionFind["DisjointSet (Union-Find)"]
        MAKE["make_set()"]
        FIND["find() with 2-Pass<br/>Path Compression"]
        UNION["union() by Rank"]
        FIN["finalize()<br/>Canonical Roots"]
    end

    subgraph Output["Output Clusters"]
        CLUSTER["Cluster Root Tag:<br/>{Address A, Address B, Address C}"]
    end

    Inputs --> CJ
    CJ -- "Yes (Mixer)" --> SKIP["Skip Transaction<br/>(Preserve Independence)"]
    CJ -- "No (Co-Spend)" --> MAKE
    MAKE --> FIND
    FIND --> UNION
    UNION --> FIN
    FIN --> CLUSTER
Loading

Module Breakdown

  • blockchain: Handles disk discovery, memory-mapping of blk*.dat files, block sequencing, and chain split/reorg resolution.
  • block & header: Zero-copy block framing and 80-byte header deserialization (version, previous block hash, Merkle root, timestamp, bits, nonce).
  • transactions & script: High-level Bitcoin transaction, input, output, and script bytecode interpreter (P2PK, P2PKH, P2SH, SegWit P2WPKH/P2WSH, Taproot P2TR, Multisig, OP_RETURN).
  • hash & hash160: Type-safe, transparent wrappers for 256-bit and 160-bit Bitcoin hashes with standard little-endian formatting.
  • visitors: Implementation of analysis plugins conforming to BlockChainVisitor.

Testing

Run the test suite:

cargo test

Run clippy linter:

cargo clippy --all-targets -- -D warnings

Research & Academic Citation

BitIodine is based on the pioneering academic research on Bitcoin transaction graph analysis and address clustering:

BitIodine: Extracting Intelligence from the Bitcoin Network
Michele Spagnuolo, Federico Maggi, and Stefano Zanero
Financial Cryptography and Data Security (FC 2014), Lecture Notes in Computer Science, vol 8437. Springer, Berlin, Heidelberg.
DOI: 10.1007/978-3-662-45472-5_29

If you use BitIodine in academic research, please cite the paper:

@inproceedings{spagnuolo2014bitiodine,
  title     = {{BitIodine}: Extracting Intelligence from the {Bitcoin} Network},
  author    = {Spagnuolo, Michele and Maggi, Federico and Zanero, Stefano},
  booktitle = {Financial Cryptography and Data Security (FC)},
  series    = {Lecture Notes in Computer Science},
  volume    = {8437},
  pages     = {457--468},
  year      = {2014},
  publisher = {Springer},
  doi       = {10.1007/978-3-662-45472-5_29}
}

Credits

The blockchain parser architecture is based on research and code originally developed by Michele Spagnuolo (miki.it) and Mathias Svensson.

About

High-performance Bitcoin blockchain parser with address clustering and graph analytics.

Topics

Resources

Stars

159 stars

Watchers

22 watching

Forks

Releases

Packages

Used by

Contributors

Languages