harpy

Educational Crystal proof-of-work blockchain tutorial
Harpy

A proof-of-work blockchain in Crystal โ€” UTXO transactions, P2P gossip, reorgs, and a Merkle anchoring layer, built for learning without cutting corners.

Crystal License: MIT Consensus: PoW Model: UTXO Formal: TLA+ Status: educational

๐Ÿš€ Quick start ยท ๐Ÿ“– Guided demo ยท โš“ Anchoring ยท ๐Ÿ“š Documentation

Harpy dashboard mining blocks

The built-in dashboard at /dashboard โ€” mining three blocks against a live node.

Harpy is a from-scratch blockchain node โ€” signed UTXO transactions, a mempool, difficulty retargeting, cumulative-work fork choice with reorgs, peer-to-peer block gossip, and a hash-anchoring/verification layer with SPV light-client proofs. It's an educational project (single operator, not mainnet-grade), but every layer is built and tested the way a real node would be: adversarial specs, a formally model-checked consensus core, and a chaos harness. Named after Harpocrates, the Greek god of silence.

What it is not: production financial infrastructure. No privacy, no external audit, no economic guarantees. Run it to understand how a chain works โ€” see docs/THREAT_MODEL.md for the honest security posture.


Features

Transactions & cryptography

  • ๐Ÿ’ธ UTXO model โ€” signed inputs/outputs, coinbase with 100-block maturity, fees, double-spend prevention (STATE_MODEL.md)
  • ๐Ÿ”‘ Ed25519 signatures with a crypto-agile address format (versioned, algorithm-tagged, checksummed) so post-quantum schemes are additive, not breaking (POST_QUANTUM_MIGRATION.md)
  • ๐Ÿงพ Mempool with validation, fee ordering, and conflict rejection

Consensus

  • โ›๏ธ Proof-of-work with configurable difficulty and time-based retargeting
  • ๐Ÿ‹๏ธ Cumulative-work fork choice โ€” heaviest chain wins, not longest (SELFISH_MINING.md, CONFIRMATION_DEPTH.md)
  • ๐Ÿ” Reorgs with per-block undo logs for exact UTXO rewind

Networking

  • ๐ŸŒ P2P block gossip with an orphan pool and multi-node convergence (P2P.md)
  • ๐Ÿ›ก๏ธ Network hardening โ€” peer caps, misbehavior banning, eclipse-attack countermeasures (SYBIL_RESISTANCE.md, ROUTING_PARTITION.md)

Storage & integrity

  • ๐Ÿ’พ Atomic writes (temp-file + rename) behind a swappable storage backend
  • ๐Ÿ”’ Checksum envelope โ€” bit-rot / tamper detected on load, before validation (STORAGE_BACKENDS.md)

Verification layer

  • โš“ Merkle anchoring API + SDK โ€” commit external record hashes on-chain, get inclusion proofs (AUDIT_LOG_ANCHORING.md)
  • ๐Ÿชถ SPV light clients โ€” verify a genesis-and-tip-pinned header chain + Merkle path, no full node

Assurance

  • โœ… TLA+ consensus spec, model-checked with TLC โ€” the fork-choice safety property holds across the state space (spec/tla)
  • ๐ŸŒช๏ธ Chaos harness โ€” partition/heal, crash/restart, and Byzantine-block fault injection (spec/chaos_harness_spec.cr)
  • ๐Ÿ” Opsec docs โ€” node hardening, key rotation, threshold multisig design (NODE_HARDENING.md)

Quick start

Prerequisites: Crystal โ‰ฅ 1.12. On Windows: winget install CrystalLang.Crystal, enable Developer Mode (for shards symlinks), restart your terminal, or run .\scripts\setup.ps1.

shards install
crystal run src/harpy.cr          # starts an HTTP node on 127.0.0.1:3000

Then open http://127.0.0.1:3000/dashboard โ€” a branded status page with live chain stats and one-click mining (shown in the GIF above).

Mine your first block and inspect the chain:

# generate a keypair for the coinbase payout, then:
curl -X POST http://127.0.0.1:3000/mine \
  -H "Content-Type: application/json" \
  -d '{"miner_pubkey":"<64-hex-ed25519-pubkey>"}'

curl http://127.0.0.1:3000/            # full chain
curl http://127.0.0.1:3000/validate    # {valid, height, work, tip}

New to it? Walk through docs/DEMO.md for a guided tour with real payloads.

HTTP API

Method & path Auth Description
GET /dashboard โ€“ Branded live status page (see GIF above)
GET / โ€“ Full blockchain as JSON
GET /health โ€“ Chain validity, last-save time, P2P/eclipse status
GET /validate โ€“ Validity, height, cumulative work, tip hash
GET /block/:index โ€“ A single full block
GET /header/:index โ€“ Block header only (light-client sync)
GET /headers?from=&to= โ€“ Range of headers
GET /proof/:index/:txid โ€“ SPV inclusion proof (header + Merkle path)
GET /mempool โ€“ Pending transactions
GET /anchor/:record_hash โ€“ Inclusion proof for an anchored record
POST /tx ๐Ÿ”‘ Submit a signed transaction to the mempool
POST /mine ๐Ÿ”‘ Mine a block { "miner_pubkey": "โ€ฆ" }
POST /anchor ๐Ÿ”‘ Anchor a record hash { "record_hash": "โ€ฆ" }

๐Ÿ”‘ = requires Authorization: Bearer <HARPY_API_KEY> (or X-API-Key) when HARPY_API_KEY is set. Write endpoints are rate-limited. See NODE_HARDENING.md.

Anchoring: hash-on-chain, data off-chain

Harpy's endgame is a verification layer: commit a record's hash on-chain and keep the data anywhere. A light client verifies it from the complete header chain between a caller-pinned genesis and independently obtained trusted tip/checkpoint, plus a Merkle proof โ€” no full node required. Genesis pinning alone does not establish canonicality.

crystal run examples/audit_log_anchoring.cr   # anchors log lines, proves inclusion, โ†’ DEMO OK
client = Harpy::AnchorClient.new(trusted_genesis_hash, trusted_tip_hash, "http://127.0.0.1:3000")
client.submit(digest)     # queue a record hash  โ†’  next POST /mine seals it into the block
client.verify(digest)     # verify proof against the independently pinned tip โ†’ true

Full walkthrough: docs/AUDIT_LOG_ANCHORING.md.

Configuration

All configuration is via environment variables.

Variable Purpose Default
HARPY_DIFFICULTY Genesis PoW difficulty (new chain only) 3
HARPY_GENESIS_TIMESTAMP Deterministic v3 genesis timestamp 2026-07-20 00:00:00 UTC
HARPY_DATA_DIR Chain file path or parent directory data/chain.json
HARPY_API_KEY Write auth for POST /tx, /mine, /anchor (unset = open)
HARPY_RATE_LIMIT Max write requests per client per window 2
HARPY_RATE_LIMIT_WINDOW Token-bucket refill interval (seconds) 10
HARPY_BIND_HOST HTTP bind address 127.0.0.1
HARPY_HTTP_PORT / PORT HTTP port 3000
HARPY_TRUST_PROXY Honor X-Forwarded-For (trusted proxy only) off
HARPY_P2P_DISABLE Set 1 to disable P2P off
HARPY_P2P_PORT P2P TCP port 9333
HARPY_P2P_PEERS Comma-separated bootstrap peers โ€“

An absent HARPY_API_KEY permits unauthenticated writes only for the default loopback development setup. An explicitly empty or whitespace-only value is a startup error; the Docker testnet always requires a non-empty key. | HARPY_ANCHOR_PEERS | Trusted peers for eclipse countermeasures | โ€“ |

Multi-node on one host:

HARPY_DATA_DIR=/tmp/a.json HARPY_HTTP_PORT=3000 HARPY_P2P_PORT=9333 crystal run src/harpy.cr
HARPY_DATA_DIR=/tmp/b.json HARPY_HTTP_PORT=3001 HARPY_P2P_PORT=9334 \
  HARPY_P2P_PEERS=127.0.0.1:9333 crystal run src/harpy.cr

CLI

Running with no arguments starts the node; subcommands operate on a chain file and exit non-zero on failure (CI-friendly):

crystal run src/harpy.cr -- verify-chain --path data/chain.json
crystal run src/harpy.cr -- export-chain --path data/chain.json --out backup.json
crystal run src/harpy.cr -- help

Architecture

flowchart LR
  W[Wallet / SDK] -->|POST /tx /mine /anchor| API[HTTP API]
  L[Light client] -->|GET /header /proof /anchor| API
  API --> MP[Mempool]
  MP --> MINER[Miner - PoW]
  ANCHOR[Anchor - Merkle roots] --> MINER
  MINER --> CHAIN[Chain - fork choice and reorg]
  CHAIN --> STATE[UTXO state and undo log]
  CHAIN --> STORE[(Storage - atomic and checksum)]
  CHAIN <-->|block gossip| P2P[P2P - orphan pool and peers]
  P2P <--> PEERS[Other nodes]
src/harpy.cr              # entry point โ†’ CLI dispatch / server
src/harpy/                # block, block_header, chain, state, utxo, mempool, miner,
                          # transaction, crypto, merkle, spv, anchor, difficulty, storage, server
src/harpy/storage/        # backend interface + atomic file backend
src/harpy/p2p/            # gossip, protocol, orphan pool, peer manager, eclipse, reputation
public/dashboard.html     # branded live status page (GET /dashboard)
public/design-system/     # full Harpy design system (tokens, fonts, icons, components)
spec/                     # unit + integration + chaos specs; spec/tla/ TLA+ model
examples/                 # runnable demos (audit-log anchoring)
docs/                     # design, security, and ops documentation

Documentation

Topic Doc
Guided demo / runbook DEMO.md
UTXO state & transactions STATE_MODEL.md
P2P gossip, reorgs, troubleshooting P2P.md
Storage integrity & backends STORAGE_BACKENDS.md
Merkle anchoring use case AUDIT_LOG_ANCHORING.md
Threat model THREAT_MODEL.md
Node hardening runbook NODE_HARDENING.md
Key rotation & compromise response KEY_ROTATION.md
Threshold multisig design THRESHOLD_MULTISIG.md
Post-quantum migration plan POST_QUANTUM_MIGRATION.md
Consensus theory SELFISH_MINING.md ยท CONFIRMATION_DEPTH.md ยท FINALITY.md
Network attacks SYBIL_RESISTANCE.md ยท ROUTING_PARTITION.md ยท POS_CHECKPOINTING.md
Incident response INCIDENT_RESPONSE.md
Brand & design system BRAND.md
Formal verification (TLA+) spec/tla/README.md
Agent-oriented guidance AGENTS.md

Development

crystal spec            # run the test suite
crystal tool format     # format sources
shards build            # build the bin/harpy binary

Model-check the consensus spec (needs Java + TLC):

cd spec/tla
java -cp tla2tools.jar tlc2.TLC -nowarning -config HarpyConsensus.cfg HarpyConsensus.tla
# โ†’ Model checking completed. No error has been found.

Status

Harpy is feature-complete across its educational roadmap:

Layer Status
PoW blocks, hashing, validation โœ…
UTXO transactions, Ed25519, mempool, fees โœ…
Difficulty retargeting, cumulative-work fork choice โœ…
P2P gossip, orphan pool, reorgs with undo โœ…
Atomic storage + checksum envelope โœ…
Merkle anchoring API, SPV, block headers โœ…
Crypto-agile addresses, node opsec docs โœ…
TLA+ model-checked consensus + chaos harness โœ…

Explored but out of scope: embedded-KV storage backend, a smart-contract VM, cross-chain bridges โ€” see the docs and roadmap issues.

License

MIT โ€” see shard.yml. An educational project; use at your own risk.

Repository

harpy

Owner
Statistic
  • 1
  • 0
  • 42
  • 0
  • 2
  • 2 days ago
  • July 2, 2026
License

Links
Synced at

Fri, 24 Jul 2026 18:57:20 GMT

Languages