Ironchain logo

Rust · post-quantum PoW blockchain

Mine it. Tamper it.
Watch it break.

Ironchain is a from-scratch proof-of-work blockchain in pure Rust with zero dependencies. The only primitive is SHA-256, implemented in the repo, and signatures are hash-based Lamport keys arranged under a Merkle tree, so it is a post-quantum, hash-only chain. This page runs the whole thing live in JavaScript, SHA-256 and all: mine real blocks, edit any field to tamper, watch the break cascade downstream, then fork the chain and watch a reorg rewrite the balances.

Open the playground View on GitHub
Pure-JS SHA-256 Account model Merkle proofs Live mining Fork choice and reorg
How to use this playground

Every control on this page is wired to a working blockchain engine. Here is what each one does.

  1. Mempool and new transactions. Use the transaction form (or the Random transaction button) to queue transfers between wallets. Pending transactions wait in the mempool until a block is mined.
  2. Difficulty slider. Sets how many leading zero bits a block hash must have. Higher means exponentially more hashing work. Watch the attempt count and hashrate change on the next mine.
  3. Mine block. Takes every pending transaction, finds a nonce that satisfies proof-of-work, pays the selected miner a coinbase reward, and appends the block. The chain view grows with an animation and shows the hash, nonce, and attempts.
  4. Tamper. Every mined block exposes editable fields (parent hash, merkle root, timestamp, difficulty, nonce, miner) and each transaction's amount, sender, and signature is editable too. Change any of them and the chain re-validates instantly: the broken block and everything downstream turns red with a reason. Hit Restore chain to undo.
  5. Merkle tree. Pick a block and a transaction to render its Merkle tree, highlight the inclusion-proof path, and verify it. The "test a non-included transaction" button shows a proof failing.
  6. Wallets. Balances and nonces update live after every mine and every reorg.
  7. Trigger fork. Builds a competing branch, extends it to greater cumulative work, and the engine performs a reorg: the active chain switches to the heavier branch and balances recompute.
Guided tour (2 minutes):
  1. Click Random transaction two or three times to fill the mempool.
  2. Click Mine block. A green block appears with a valid proof-of-work hash.
  3. Mine one or two more blocks so you have a chain.
  4. In any earlier block, edit a transaction amount, and that block and all blocks after it turn red. Click Restore chain.
  5. Open the Merkle tree panel, pick a transaction, and watch its inclusion path light up.
  6. Click Trigger fork and watch the chain reorganize onto the heavier branch.

Controls

Mine

Difficulty

Expected work ≈ 212 = 4096 hashes per block. Above ~18 bits the browser may pause noticeably.

Last mine

...Attempts
...Hashes / s
...Cumulative work

Wallets

Balances recompute from the active chain after every mine and reorg.

NameAddressBalanceNonce

Mempool

Pending transactions. Cleared into the next mined block.

The chain

Newest block on the right. Green = valid, red = invalid. Edit any field to tamper; the break cascades downstream. Fields are recomputed and re-validated on change.

Signatures here are a keyed hash (MAC): SHA256(secret ‖ from ‖ to ‖ amount ‖ nonce). Tampering with the amount, sender, or signature makes it un-recomputable and the block invalid. The real Ironchain engine uses hash-based Lamport + Merkle (XMSS) one-time signatures. This browser stand-in keeps the tamper demo synchronous and simple.

Merkle tree and inclusion proof

A block commits to its transactions with a Merkle root. Pick a block and a transaction to see the proof path that links a single leaf to the root. Odd levels duplicate the last node.

Activity log

Valid accepts, any tamper rejects

The point of Ironchain is that its safety is machine-checked, not asserted. The tamper oracle in tests/tamper_oracle.rs builds a valid multi-block chain, confirms full-chain validation accepts it, then mutates one copy of every mutable field in turn and confirms validation rejects each one. Six gates plus an adversarial suite and a soak back it up.

# the correctness gates (cargo test)
1 tamper oracle     valid accepts; mutate any amount, fee, signature, sender,
                     receiver, nonce, merkle root, parent hash, PoW nonce,
                     timestamp, difficulty, or miner, and it rejects
2 signatures        one flipped bit in the message or signature fails, with
                     pinned SHA-256 and derived-address known-answer vectors
3 merkle            inclusion proofs verify for included leaves, fail otherwise,
                     across non-power-of-two trees and side-swapped proofs
4 fork choice       the most-work chain is picked, and the resulting account
                     state matches an independent recomputation on the winner
5 retarget          the incremental and full-chain validators recompute the
                     same difficulty at every retarget boundary
6 malleability      every reveal, complement, and auth-path bit is flipped and
                     must fail; wrong leaf indices and truncated paths too

The signatures are the different angle: instead of elliptic-curve keys, Ironchain signs with Lamport one-time keys under a Merkle tree, so security rests only on the hardness of reversing or colliding SHA-256. The chain stays secure even against a quantum adversary. The adversarial suite in tests/adversarial.rs states one rule per test, and tests/soak.rs mines hundreds of blocks while a rival miner forks and overtakes.

How it differs

Ironchain is not a new consensus idea. It is the readable, zero-dependency version of ideas that already secure real chains, with one deliberate change at the signature layer.

Bitcoin (PoW and Merkle)

Ironchain echoes the same core: proof-of-work over a block header, a transaction Merkle tree with inclusion proofs, difficulty retargeting, and fork choice by most cumulative work. The difference is scale and intent. Bitcoin is a hardened production network; Ironchain is a single crate you can read in an afternoon.

Elliptic-curve chains

Nearly every chain signs with elliptic-curve keys, which a large quantum computer could break. Ironchain signs with Lamport one-time keys under a Merkle tree, an XMSS-style scheme, so security rests only on the hardness of reversing or colliding SHA-256. It stays secure even against a quantum adversary.

Teaching toys

Most from-scratch blockchains skip the parts that make a chain safe: real signatures, inclusion proofs, retarget consistency, reorg. Ironchain keeps all of them and proves each with a machine-checkable test rather than prose.

The only cryptographic primitive is SHA-256, implemented in the repository. No curves, no external crates, standard library only.

The building blocks

Every primitive a real chain needs, from scratch.

Each one is implemented in the repository behind no dependency, and each is covered by the correctness gates.

consensus Proof-of-work mining

The miner searches for a header nonce whose double SHA-256 hash meets a difficulty target measured in leading zero bits. block::mine and meets_target.

consensus Difficulty retarget

The target recomputes on a schedule. Gate five confirms the incremental and full-chain validators agree at every retarget boundary, for chains that speed up and slow down.

integrity Merkle tree

Each block commits to its transactions with a Merkle root. merkle::prove and verify produce and check inclusion proofs across non-power-of-two trees.

signatures Lamport under Merkle

Hash-based one-time keys arranged under a Merkle key tree, an XMSS-style scheme. Post-quantum, SHA-256 only. Signatures serialize and reparse strictly.

mempool Mempool

Pending transactions wait in the mempool with projected balances and nonces until a miner includes them in the next block. chain::submit_tx.

consensus Fork choice and reorg

The node selects the branch with the most cumulative work and reorganizes onto it, recomputing account state to match an independent recomputation on the winner.

light client SPV proofs

Prove a payment is in a block from just the header and a Merkle path, no chain storage required. spv::prove_tx and verify_spv, with a compact serialization that round trips.

Use it

A library crate plus a binary, standard library only. The same engine backs the demo, the light client, and the gates.

Demo binary

./target/release/ironchain mines a short chain with random valid transactions, prints the blocks and balances, then tampers with a transaction and shows validation rejecting it.

Library crate

Modules sha256, sig, merkle, tx, block, state, chain, and spv. Build a Blockchain, submit_tx, mine_next, read state, or check a whole history with validate_chain.

SPV light client

spv::prove_tx builds a proof from a block and a transaction index; spv::verify_spv checks a payment with no chain present. Every truncation and non-canonical flag is rejected.

Correctness gates

cargo test runs the tamper oracle, six gates, the adversarial suite, and the SPV tests. Env knobs scale the oracle and a soak mines hundreds of blocks under a forking rival.

# build, run the gates, and run the demo binary
cargo build --release
cargo test
./target/release/ironchain        # mines a chain, then tampers and shows rejection

# scale the tamper oracle and gates with env knobs
IRONCHAIN_FUZZ_OPS=12 IRONCHAIN_SEED=7 cargo test

# the long-horizon reorg soak
IRONCHAIN_SOAK=1 cargo test --release --test soak