MineRace — Build Your Own Blockchain
Build a complete balance-based blockchain from scratch in JavaScript. Create wallets, send transactions, compete to mine blocks against bots, visualise the chain in real-time, and chat with an AI agent — all running in Docker.
Context & Motivation
You've completed Part 1 of the course. Over six modules, you learned how hashing works, how keys and signatures secure transactions, how blocks are chained together, how proof of work creates consensus, and how networks handle forks. Now it's time to assemble everything into a working blockchain you can interact with.
Apply What You Learned
This is not new theory. Every line of code you write maps directly to a concept from Modules 0–6: hashing (M2), signatures (M2), transactions (M3), blocks (M3), mining (M4), and consensus (M5).
Bridge to Ethereum
You'll use a balance/account model instead of UTXO. This is exactly how Ethereum works. When you start Part 2, you'll already think in Ethereum's native paradigm.
What You'll Build
A full-stack blockchain application: a blockchain engine (balance model, PoW, validation), a REST API (Express), a visual frontend with wallet management, mining competition, and chain explorer, an AI agent (Ollama, running locally for free), and your own mining bot — all containerised with Docker.
Balance Model vs UTXO
In Module 3, you built a cryptocurrency using the UTXO model (Unspent Transaction Outputs), which is how Bitcoin works. In this project, you'll use the balance/account model instead. Here's the difference:
| UTXO (Bitcoin) | Balance (This Project / Ethereum) | |
|---|---|---|
| How balance is tracked | Sum of unspent outputs scattered across blocks | Single number per address, like a bank account |
| To send 30 coins | Consume a 50-coin UTXO, create 2 outputs: 30 to recipient + 20 change back | Debit sender by 30, credit recipient by 30 |
| Complexity | More complex: track all UTXOs, handle change | Simpler: just read/write balances |
| Privacy | Better: can use new address for each change output | Weaker: one address, all history visible |
| Used by | Bitcoin, Litecoin, Cardano | Ethereum, Solana, BSC |
Why This Matters for Part 2
Ethereum uses the balance model. Every Ethereum address (EOA or contract) has a balance stored in the world state — a giant key-value map of address → {balance, nonce, code, storage}. By building your blockchain with balances now, you're already thinking the way Ethereum thinks. In Part 2, you'll see this exact pattern again when you interact with smart contracts. Note: Ethereum accounts also have a nonce (transaction counter) to prevent replay attacks. This project omits nonce for simplicity — you'll implement it in Part 2.
Architecture Overview
Data Storage — One File Per Block
Each block is saved as a separate JSON file on disk, inside a data/blocks/ folder. Block 0 is block-0.json, block 1 is block-1.json, and so on. No database. No abstraction. Just files you can see, open, read, and edit with any text editor.
Why files instead of a database or in-memory array? Because you can physically interact with them:
ls data/blocks/
See your chain grow as new files appear
cat data/blocks/block-3.json
Read any block's raw content
nano block-3.json
Change the amount of a transaction, save — you just tampered with the chain. Run validation and watch it break
rm block-5.json
The chain is now incomplete. The server detects this on startup
docker-compose restart
The chain persists across restarts — your blocks are still there
How It Works
On startup, the server reads all block-*.json files from data/blocks/, sorts them by index, validates the chain, and rebuilds the balance map. If no files exist, it creates the genesis block (block-0.json). When a new block is mined and accepted, the server writes it to disk immediately. The in-memory chain and the files are always in sync.
Configuration
Create a config.js file at the root of your project. All tuneable parameters live here: mining difficulty, mining reward amount, bot count, bot names, bot speed (interval in ms), whether bots include transactions, server port. The server reads this file on startup. Changing a value and restarting the server changes the network's behaviour.
Project Structure
minerace/
├── config.js # All tuneable parameters
├── server.js # Express app entry point
├── blockchain/
│ ├── Block.js # Block class
│ ├── Blockchain.js # Chain + balances + mempool + validation
│ ├── Transaction.js # Transaction class (sign/verify)
│ └── Wallet.js # Key pair generation (ECDSA)
├── bots/
│ └── botMiner.js # Server-side bot mining logic
├── routes/
│ ├── chain.js # GET /api/chain, /api/chain/:index
│ ├── mining.js # GET /api/challenge, POST /api/submitBlock
│ ├── transactions.js # POST /api/transaction, GET /api/mempool
│ └── chat.js # POST /api/chat (Ollama integration)
├── data/
│ └── blocks/ # ONE JSON FILE PER BLOCK
│ ├── block-0.json # Genesis block (created on first start)
│ ├── block-1.json # Mined by Bot-Paris
│ ├── block-2.json # Mined by you
│ └── ...
├── public/ # Served statically by Express
│ ├── index.html # Main frontend page
│ ├── style.css # Styles
│ ├── app.js # Frontend logic (wallets, UI, polling)
│ └── miner-worker.js # Web Worker for browser-side mining
├── my-bot.js # YOUR custom mining bot (deliverable)
├── test-step1.js ... test-step10.js # Test files (provided)
├── Dockerfile
├── docker-compose.yml
└── README.mdTechnology Stack
Mining & Competition
The server is the single source of truth — it holds the canonical chain, the public mempool, and validates all submitted blocks. But anyone can compete to mine the next block. The server doesn't care how you mine — it only cares that your block is valid and that you're first.
Three Ways to Mine
Browser UI
Click the "Mine" button in the frontend. A Web Worker starts hashing, the nonce counter spins visually. You watch proof of work happen in real-time.
Server Bots
Built-in bots (Bot-Paris, Bot-Tokyo, Bot-NYC) mine empty blocks by default — they don't grab transactions from the mempool, but they still earn mining rewards. Their speed, count, and behaviour are fully configurable.
Your Own Bot
Write a script (JS, Python, anything) that calls GET /api/challenge, grabs the mempool, assembles a block, finds a valid nonce, and submits via POST /api/submitBlock. If your bot includes transactions and the server bots don't — you control what gets confirmed.
The Mining Race
Challenge announced: The server exposes the current challenge via GET /api/challenge — the previous block's hash, the difficulty target, the current mempool, and the next block index. Anyone can read it.
Miners compete: The browser UI, server bots, and student bots all start hashing simultaneously. Each tries to find a nonce that makes the block hash start with the required number of zeros.
First valid block wins: The first miner to POST /api/submitBlock with a valid block gets it added to the chain. The server validates: correct previous hash? Valid PoW? All included transactions signed and funded? Exactly one mining reward of 50 coins?
Losers start over: If you submit a block but someone was faster, the server responds: "Block N already mined by Bot-Paris, start on block N+1". Your previousHash is stale — you must call /api/challenge again.
What a Block Looks Like
{
"index": 3,
"timestamp": 1711878400,
"previousHash": "000a3f8c2e...",
"transactions": [
{
"from": "04a1b2c3d4e5...", // sender public key
"to": "04f6a7b8c9d0...", // recipient public key
"amount": 20,
"signature": "3045022100..." // ECDSA signature
},
{
"from": "MINING_REWARD", // special: coinbase tx
"to": "04a1b2c3d4e5...", // miner's public key
"amount": 50,
"signature": null // no signature needed
}
],
"nonce": 847,
"hash": "000f8c2a7b..." // starts with "000" (difficulty = 3)
}About Server Bots
Bots mine empty blocks by default: they don't include transactions from the mempool, they just find a valid nonce and collect the mining reward. This means pending transactions stay in the mempool until someone (you, via UI or bot) includes them in a block. Everything is configurable: number of bots, their speed, names, and whether they include transactions. Experiment with the settings to see how competition intensity affects the network.
Key Insight
The blockchain doesn't care how you mine. It doesn't care if you clicked a button, wrote a Python script, or built a GPU farm. It only validates the result: is the hash valid? Is the block well-formed? Are the transactions legitimate? That's the beauty of a permissionless protocol — the rules are the same for everyone.
10 Mini-Goals
Build incrementally. Each step is standalone testable — you code it, test it, confirm it works, then move on. A test file is provided for each step. Don't skip ahead.
Block & Chain
M3Create a Block class (index, timestamp, previousHash, transactions, nonce, hash) and a Blockchain class with a genesis block. Each block's hash is computed from its contents using SHA-256. The chain is kept in memory as an array, but every block is also written to disk as a JSON file in data/blocks/ (block-0.json, block-1.json, ...). On startup, the chain is rebuilt by reading all block files from disk.
Proof of Work
M4Add a mineBlock(difficulty) method. It increments the nonce until the hash starts with difficulty zeros. Start with difficulty 2 ("00") in config.js. If mining is instant (<0.5s), increase to 3 ("000"). If it takes >10s, decrease to 2. The goal: 1–3 seconds per block, enough to feel the work without waiting.
Wallet (Keys & Signatures)
M2Create a Wallet class using the elliptic npm package with the secp256k1 curve (same as Bitcoin). It generates a key pair, can sign(hash) a SHA-256 digest, and anyone can verify(hash, signature, publicKey). The public key (hex, starting with 04) is the wallet's address. Use the same library on server and browser to avoid format mismatches.
Transactions & Balances
M3Create a Transaction class (from, to, amount, signature). The blockchain tracks balances in a balances = {} map. Before accepting a transaction into the mempool: (1) verify the signature against the sender's public key, (2) compute available balance = confirmed balance (from mined blocks) minus sum of amounts in pending mempool txs from this sender, (3) reject if available < amount. Example: if Alice has 100 confirmed coins and 60 pending in the mempool, her available balance is 40 — a 50-coin tx is rejected.
Mining Rewards & Full Cycle
M3–M4When a block is mined, the miner includes a special coinbase transaction: from: "MINING_REWARD", to: minerAddress, amount: 50. The server validates: exactly one reward tx per block, amount must be 50, no signature needed. This is the only way coins enter the system.
REST API (Express)
PrerequisiteWrap the blockchain engine in an Express server. Expose all endpoints: chain, balance, mempool, challenge, transaction submission, block submission, chat, and stats. Test each endpoint with curl.
Bot Miners
M4–M5Add server-side bots that mine automatically. They run on intervals, find valid nonces, and submit empty blocks (reward only, no transactions). Make them configurable: number of bots, speed (interval in ms), names. Bots should be enabled/disabled via config.
Frontend & Visual Blockchain
PrerequisiteBuild the web interface: wallet management (create, select, see balance), transaction form (recipient, amount, sign & send), mine button with live nonce counter, and a visual blockchain explorer. Use SVG or Canvas for the chain visualisation — blocks as connected nodes, colour-coded by miner, expandable to show transactions. Add a leaderboard showing mining stats.
AI Agent (Ollama)
NewIntegrate a local LLM via Ollama (runs in Docker, free, no API key). The /api/chat endpoint sends the user's question plus a chain context summary (current block height, all balances, recent transactions, mining stats) to the LLM. The AI can answer: "Who has the most coins?", "Who mined block 5?", "Is the chain valid?", "Explain the last transaction".
Docker & Containerisation
RequiredCreate a Dockerfile for the app and a docker-compose.yml with two services: app (Node.js + Express + frontend + bots) and ollama (local LLM). One command to start everything: docker-compose up. Open the browser, and the full system is running.
docker-compose.yml Reference
docker-compose.yml
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- ./data:/app/data # blocks persist on host disk
environment:
- OLLAMA_HOST=http://ollama:11434
depends_on:
- ollama
ollama:
image: ollama/ollama
ports:
- "11434:11434"
volumes:
- ollama-data:/root/.ollama # model cache persists
# Pull a model on first start:
# docker exec minerace-ollama-1 ollama pull llama3.2
volumes:
ollama-data:Important: Pull the Model
After docker-compose up, the Ollama container is running but has no model yet. You need to pull one: docker exec minerace-ollama-1 ollama pull llama3.2 (about 2 GB). This only needs to be done once — the model is cached in the ollama-data volume. Alternatively, add a startup script in your Dockerfile that pulls the model automatically.
What Persists, What Doesn't
Blocks are saved to data/blocks/ and survive restarts. But the mempool is in memory only — pending transactions are lost if the server restarts. This is by design: just like Bitcoin, unconfirmed transactions are volatile. Miners should include pending transactions in blocks quickly, or they risk losing them. This is a teaching moment, not a bug.
REST API Reference
The API is the protocol. It's what the browser uses, what the bots use, and what your custom bot will use. Everyone speaks the same language.
| Endpoint | Method | Description |
|---|---|---|
GET /api/chain |
GET | Full chain (all blocks) |
GET /api/chain/:index |
GET | Single block by index |
GET /api/balance/:address |
GET | Confirmed + available balance for an address |
GET /api/mempool |
GET | Pending transactions (public) |
GET /api/challenge |
GET | Current mining challenge: previousHash, difficulty, pendingTxs, blockIndex |
POST /api/transaction |
POST | Submit a signed transaction to the mempool |
POST /api/submitBlock |
POST | Submit a mined block — first valid submission wins |
POST /api/chat |
POST | Ask the AI agent about the chain state |
GET /api/stats |
GET | Mining stats: who mined what, win rates, leaderboard |
Request & Response Examples
POST /api/transaction
Content-Type: application/json
{
"from": "04a1b2c3d4e5f6...", // sender public key (hex)
"to": "04f6a7b8c9d0e1...", // recipient public key (hex)
"amount": 20,
"signature": "3045022100abc..." // ECDSA signature of "04a1...04f6...20"
}
Response (201): { "message": "Transaction added to mempool" }
Response (400): { "error": "Insufficient balance (have: 10, need: 20)" }
Response (400): { "error": "Invalid signature" }POST /api/submitBlock
Content-Type: application/json
{
"index": 4,
"timestamp": 1711878400,
"previousHash": "000a3f8c2e7b...",
"transactions": [
{ "from": "04a1...", "to": "04f6...", "amount": 20, "signature": "3045..." },
{ "from": "MINING_REWARD", "to": "04a1...", "amount": 50, "signature": null }
],
"nonce": 1847,
"hash": "0002f8c1a3..."
}
Response (201): { "message": "Block 4 accepted", "miner": "04a1..." }
Response (409): { "error": "Block 4 already mined by Bot-Paris" }
Response (400): { "error": "Invalid previousHash — expected 000f8c..., got 000a3f..." }Wallet Address Format
A wallet address is the full public key in hex (uncompressed secp256k1, starting with 04). To sign a transaction, compute SHA-256(from + to + amount) with no delimiter (e.g. SHA256("04a1b2...04f6a7...20")), then sign the resulting hash. Signatures are DER-encoded hex strings. The server verifies the same hash with the sender's public key. Truncate in the UI (e.g. 04a1b2...e5f6) but always use the full key for cryptographic operations.
CORS
Since the frontend is served by the same Express server (from public/), you won't have CORS issues for browser requests. But if your custom mining bot runs as a separate Node.js script or from another origin, add cors() middleware to Express: app.use(require('cors')()). This is one line but easy to forget.
Server Validation Rules
When a block is submitted via POST /api/submitBlock, the server checks: (1) previousHash matches the current chain tip, (2) the hash starts with the required number of zeros, (3) all included transactions have valid signatures and sufficient balances, (4) there is exactly one mining reward transaction with amount = 50, (5) no one has already mined this block index. If any check fails, the block is rejected with a clear error message.
Example: GET /api/stats Response
{
"blockHeight": 12,
"difficulty": 3,
"chainValid": true,
"miners": [
{ "address": "Bot-Paris", "blocksMined": 5, "earnings": 250 },
{ "address": "Bot-Tokyo", "blocksMined": 3, "earnings": 150 },
{ "address": "04a1b2...e5f6", "blocksMined": 4, "earnings": 200 }
],
"mempool": {
"pending": 2,
"totalAmount": 45
}
}How the Frontend Discovers Addresses
The recipient dropdown in the transaction form is populated by fetching GET /api/chain and extracting all unique from/to values from every transaction across all blocks. This includes bot addresses and other wallets. The frontend should refresh this list periodically (every 5s) to catch new miners and recipients.
Frontend & AI Agent
The frontend is where the magic becomes visible. It's pure HTML, CSS, and JavaScript — no framework. The UI should make the student feel like a participant in a real blockchain network.
User Interface
Wallet Panel
Create multiple wallets (key pairs generated in the browser). Switch between them. See each wallet's balance. The private key never leaves the browser — transactions are signed client-side.
Transaction Panel
Select recipient address, enter amount, sign the transaction client-side, and broadcast to the server. See the transaction appear in the mempool. Watch it get confirmed when the next block is mined.
Mining Panel
Click "Mine" and watch the nonce counter increment live. Mining runs in a Web Worker (miner-worker.js) so it doesn't freeze the UI. The worker receives the block data via postMessage(), hashes in a loop, and posts back progress (nonce count) and the result (valid hash + nonce). See who wins the race — you or a bot. When a block is mined (by anyone), it appears in the chain visualisation in real-time.
AI Chat Panel
Ask the AI anything about the chain: "Who has the most coins?", "Is block 4 valid?", "Summarise the last 5 transactions". The AI has full context of the current chain state.
Crypto: Exact Specification
Recommended approach: use the elliptic npm package on both server and browser (avoids format mismatch). Use the secp256k1 curve (same as Bitcoin). Generate key pairs with ec.genKeyPair(), sign with key.sign(hash), verify with key.verify(hash, signature). The public key (hex, uncompressed, starting with 04) is the wallet address. To sign a transaction, hash the string from + to + amount concatenated with no delimiter (e.g. "04a1b2...04f6a7...20") using SHA-256, then sign the hash. Signatures are DER-encoded hex strings. Private keys stay in localStorage and are never sent to the server.
Visual Blockchain Explorer
This is the centrepiece of your frontend. Use SVG or Canvas to render the blockchain as a visual graph:
Block Nodes
Connected by arrows showing the previousHash link
Colour-Coded
Your blocks are one colour, each bot has its own
Expandable
Click a block to see transactions, nonce, hash, timestamp
Live Updates
New blocks animate into view as they are mined
Chain Validation
Valid blocks glow green, broken blocks turn red
Leaderboard
Who mined the most blocks, who earned the most rewards
Be Creative
The visual explorer is where you can shine. Animate block additions, show hash connections with dotted lines, use particle effects when a block is mined, show the nonce search as a progress bar. The more intuitive and beautiful your visualisation, the better. SVG is recommended for crisp rendering, Canvas for performance with many blocks.
AI Agent (Ollama)
The AI agent runs locally via Ollama — no API key, no cost. It runs as a Docker container alongside your app. The /api/chat endpoint builds a system prompt with:
Then appends the user's question and sends everything to Ollama. The model generates a contextualised answer. Use llama3.2 or mistral — small models that run on any laptop.
How to Call Ollama
Ollama exposes a simple HTTP API. In your Docker network, the Ollama container is reachable at http://ollama:11434. Your backend's /api/chat handler does this:
// routes/chat.js (server-side, Node.js)
app.post('/api/chat', async (req, res) => {
const { message } = req.body;
// 1. Build chain context from the blockchain engine
const context = buildChainContext(blockchain);
// e.g. "Block height: 12. Difficulty: 3.
// Balances: 04a1b2...=180, 04f6a7...=70, Bot-Paris=250
// Last 3 txs: 04a1... sent 20 to 04f6... (block 11) ..."
// 2. Call Ollama's API
const response = await fetch('http://ollama:11434/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'llama3.2', // or 'mistral'
prompt: message,
system: `You are a blockchain assistant. Here is the current
state of the chain:\n\n${context}\n\n
Answer the user's question based on this data.`,
stream: false // wait for full response
})
});
const data = await response.json();
res.json({ reply: data.response });
});What buildChainContext() Returns
The quality of your AI depends on this function. It reads the in-memory chain and produces a text summary like this:
// Example output of buildChainContext(blockchain)
Blockchain Status:
- Block height: 12
- Difficulty: 3
- Chain valid: yes
Balances:
- 04a1b2...e5f6: 180 coins (mined 4 blocks)
- 04f6a7...d0e1: 70 coins (mined 0 blocks)
- Bot-Paris: 250 coins (mined 5 blocks)
- Bot-Tokyo: 150 coins (mined 3 blocks)
Last 5 transactions:
- Block 12: 04a1b2... sent 20 to 04f6a7...
- Block 12: MINING_REWARD → 04a1b2... (50 coins)
- Block 11: Bot-Paris mined empty block (reward only)
- Block 10: 04f6a7... sent 10 to 04a1b2...
- Block 10: MINING_REWARD → Bot-Tokyo (50 coins)
Mempool (pending): 1 transaction
- 04a1b2... → 04f6a7...: 15 coins (waiting to be mined)The Key Pattern
The LLM doesn't "read" the blockchain directly. Your backend reads the chain (it has direct access to the Blockchain object in memory), serialises it into a text summary, and injects it into the system prompt. The LLM then answers questions based on that text. This is the same pattern used in RAG systems — give the model context, then ask a question. The quality of your AI agent depends entirely on how well you build the buildChainContext() function.
Graceful Fallback
Ollama may be slow (first request loads the model into memory), unreachable (container not started), or fail (model not pulled yet). Your /api/chat must handle all cases: set a 30-second timeout on the fetch call (signal: AbortSignal.timeout(30000)), catch errors, and return a helpful JSON response like {"error": "AI unavailable", "hint": "Run: docker exec minerace-ollama-1 ollama pull llama3.2"}. The rest of the app must work perfectly without the AI — it's an enhancement, not a dependency.
Deliverables & Grading
Required Deliverables
1 Blockchain Engine
Balance-based blockchain with PoW, transaction signing/verification, balance tracking, mining rewards, and chain validation. Pure JavaScript, no external blockchain libraries.
2 REST API
Express server exposing all endpoints (chain, balance, mempool, challenge, transaction, submitBlock, chat, stats). Proper error messages for invalid submissions.
3 Frontend & Visual Explorer
Wallet management, transaction form, mining UI with live nonce counter, visual blockchain (SVG/Canvas), chain validation display, and leaderboard. Responsive design.
4 Custom Mining Bot
A student-written script that calls the API, assembles blocks from the mempool, mines, and submits. This proves you understand the protocol at the API level.
5 AI Agent (Ollama)
Chat endpoint that gives the LLM full chain context and returns meaningful answers about blockchain state. Graceful fallback if Ollama is unavailable.
6 Docker & Documentation
Dockerfile + docker-compose.yml. One command to start everything. README with setup instructions, architecture diagram, and screenshots or demo video.
Grading Criteria
| Criterion | Weight | Description |
|---|---|---|
| Blockchain Engine | 25% | Balance model, PoW, signature verification, chain validation, mining rewards |
| REST API | 15% | All endpoints working, proper validation and error messages |
| Frontend & Visual Explorer | 20% | Wallet, transactions, mining UI, visual chain (SVG/Canvas), leaderboard |
| Custom Mining Bot | 15% | Script that mines via API, includes transactions, competes with bots |
| AI Agent | 10% | Contextual answers, chain-aware responses, graceful Ollama fallback |
| Docker & Docs | 10% | docker-compose up works, README clear, architecture diagram included |
| Code Quality | 5% | Clean code, modular structure, no hardcoded secrets, consistent naming |
Optional Extensions Bonus
P2P Simulation
Run 2+ app containers in Docker that gossip blocks to each other. Simulate a real multi-node network with fork resolution.
Difficulty Adjustment
Dynamically adjust mining difficulty based on block time (like Bitcoin). If blocks come too fast, increase difficulty; too slow, decrease it.
Block Explorer Page
A dedicated page with searchable transaction history per address, block details, and a timeline of all mining activity.
Transaction Fees
Add optional transaction fees. Miners can prioritise higher-fee transactions. Introduces the concept of a fee market.
Chain Tampering Sandbox
A UI feature that lets the user modify a block's data and see the chain validation break in real-time. Visually demonstrates immutability.
Mining Race Dashboard
Real-time dashboard showing all active miners, their hash rates, who's closest to finding a block, and historical win rates with charts.
Module-by-Module Alignment
Every piece of this project maps directly to a concept you learned in Part 1:
| Module | Concept | Application in Project |
|---|---|---|
| M0 | Transaction & block anatomy | Block structure, transaction structure, chain explorer |
| M1 | The double-spend problem | Balance validation, overdraft rejection |
| M2 | SHA-256, ECDSA, digital signatures | Block hashing, wallet key pairs, transaction signing |
| M3 | Building a cryptocurrency | Full blockchain engine (balances, transactions, blocks) |
| M4 | Proof of Work, mining, consensus | Mining competition, bot miners, block validation |
| M5 | Network, forks, governance | Stale block handling, chain tip management |
| M6 | Balance model (Ethereum) | Balance/account model instead of UTXO |
Summary
This project takes everything you learned in Part 1 and turns it into a real, interactive blockchain you can see, touch, and compete on. You'll hash blocks, sign transactions, race against bots, and chat with an AI about your chain — all from your browser, all running in Docker. When you're done, you'll have built a blockchain from scratch. Not read about it. Not studied it. Built it. Good luck, and may the best miner win.