mhinparser 0.1.1

High-performance Bitcoin blockchain parser implementing the My Hash Is Nice protocol
mhinparser-0.1.1 is not a library.

mhinparser

Tests Coverage Format Clippy Crates.io Docs.rs

A high-performance Bitcoin blockchain parser implementing the My Hash Is Nice protocol.

Built on mhinprotocol, this parser leverages protoblock for blazing-fast block fetching and rollblock for efficient UTXO management with instant rollback support.

Features

  • πŸš€ Maximum Performance β€” Parallel block fetching via protoblock with configurable thread pools
  • ⚑ Instant Rollbacks β€” rollblock provides chain reorganization handling
  • 🌐 Multi-Network β€” Supports Mainnet, Testnet4, Signet, and Regtest
  • πŸ“Š Live Progress β€” Terminal UI powered by ratatui
  • πŸ”§ Daemon Mode β€” Run as a background service with signal handling

Installation

cargo install mhinparser

Or build from source:

git clone https://github.com/ouziel-slama/mhinparser
cd mhinparser
cargo build --release

Quick Start

# Parse mainnet (connects to local Bitcoin Core RPC)
mhinparser

# Parse testnet4
mhinparser --network testnet4

# Run as daemon
mhinparser --daemon

# Stop daemon
mhinparser stop

Configuration

Configuration can be provided via CLI flags, environment variables, or a TOML file.

Default config location: ~/.config/myhashisnice/mhinparser/mhinparser.toml

Example configuration with production-optimized values (defaults are more conservative):

# Network: mainnet, testnet4, signet, regtest
network = "mainnet"

# Data directory for SQLite stats and rollblock storage
data_dir = "/path/to/data"

[protoblock]
rpc_url = "http://127.0.0.1:8332"
rpc_user = "bitcoin"
rpc_password = "password"
thread_count = 8              # default: 4
max_batch_size_mb = 256       # default: 10

[rollblock]
user = "mhin"                 # change this in production
password = "mhin"             # change this in production
port = 9443
shards_count = 16
thread_count = 4
initial_capacity = 100_000_000  # default: 5_000_000

The embedded rollblock server binds to 127.0.0.1 with basic authentication. By default it uses the mhin/mhin credentials on port 9443; change these via --rollblock_user, --rollblock_password, and --rollblock_port. The parser emits a startup warning when the defaults are still in use.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       mhinparser                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  protoblock β”‚  β”‚ mhinprotocolβ”‚  β”‚     rollblock       β”‚  β”‚
β”‚  β”‚  ───────────│  β”‚  ───────────│  β”‚  ─────────────────  β”‚  β”‚
β”‚  β”‚  Fast block β”‚  β”‚  MHIN       β”‚  β”‚  UTXO store with    β”‚  β”‚
β”‚  β”‚  fetching & β”‚  β”‚  protocol   β”‚  β”‚  instant rollback   β”‚  β”‚
β”‚  β”‚  processing β”‚  β”‚  logic      β”‚  β”‚  support            β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                           β”‚                                  β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”                          β”‚
β”‚                    β”‚   SQLite    β”‚                          β”‚
β”‚                    β”‚   (stats)   β”‚                          β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CLI Reference

Usage: mhinparser [OPTIONS] [COMMAND]

Commands:
  run   Start the parser (default)
  stop  Stop the running daemon

Options:
  -c, --config <FILE>       Path to config file
  -d, --data-dir <DIR>      Data directory
  -n, --network <NETWORK>   Bitcoin network [mainnet|testnet4|signet|regtest]
      --daemon              Run as background daemon
  -h, --help                Print help
  -V, --version             Print version

Requirements

  • Rust 1.75+ (2021 edition)
  • Bitcoin Core node with RPC enabled
  • ~50GB+ disk space for mainnet UTXO set

Data Storage

SQLite Stats Database

The parser stores protocol statistics in mhinstats.sqlite3 with two tables:

-- Individual rewards per block
CREATE TABLE rewards (
    block_index INTEGER NOT NULL,
    txid TEXT NOT NULL,
    vout INTEGER NOT NULL,
    zero_count INTEGER NOT NULL,
    reward INTEGER NOT NULL,
    PRIMARY KEY (block_index, txid, vout)
);

-- Per-block and cumulative stats (JSON-encoded)
CREATE TABLE stats (
    block_index INTEGER PRIMARY KEY,
    block_stats TEXT NOT NULL,   -- {"block_index":..., "total_reward":..., "reward_count":..., ...}
    cumul_stats TEXT NOT NULL    -- cumulative totals up to this block
);

Example: Query rewards with sqlite3

sqlite3 /path/to/data/mhinstats.sqlite3 \
  "SELECT txid, zero_count, reward FROM rewards WHERE block_index = 870000;"

Rollblock UTXO Store

UTXOs are stored in a rollblock key-value store for O(1) lookups and instant rollbacks.

Key format: Each UTXO is identified by an 8-byte key computed as:

// From mhinprotocol:
fn compute_utxo_key(txid: &Txid, vout: u32) -> [u8; 8] {
    let mut payload = [0u8; 36];
    payload[..32].copy_from_slice(txid.as_ref());       // 32-byte txid
    payload[32..].copy_from_slice(&vout.to_le_bytes()); // 4-byte vout (LE)
    xxh64(&payload, 0).to_le_bytes()                    // xxHash64 β†’ 8 bytes
}

Value format: The MHIN UTXO balance stored as a little-endian u64.

Example: Read UTXO balances with rollblock client

use rollblock::client::{ClientConfig, RemoteStoreClient};
use rollblock::net::BasicAuthConfig;
use xxhash_rust::xxh64::xxh64;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to rollblock server
    let auth = BasicAuthConfig::new("your_user", "your_password");
    let config = ClientConfig::without_tls(auth);
    let mut client = RemoteStoreClient::connect("127.0.0.1:9443", config)?;

    // Compute UTXO key for txid:vout
    let txid_hex = "abc123..."; // your txid
    let vout: u32 = 0;
    let mut payload = [0u8; 36];
    payload[..32].copy_from_slice(&hex::decode(txid_hex)?);
    payload[32..].copy_from_slice(&vout.to_le_bytes());
    let key = xxh64(&payload, 0).to_le_bytes();

    // Fetch balance
    let value = client.get_one(key)?;
    let balance = u64::from_le_bytes(value.try_into().unwrap_or([0; 8]));
    println!("Balance: {} sats", balance);

    client.close()?;
    Ok(())
}

License

Licensed under either of:

at your option.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.