
JER Cryptocurrency: Bitcoin Fork with Production-Grade Genesis Mining
Creating a cryptocurrency is more than just forking Bitcoin's code. The genesis block—the very first block in your blockchain—requires proof-of-work mining just like any other block. For production security, this means running billions of SHA-256 hashes until you find one that meets Bitcoin's difficulty target.
In this article, I'll share my journey mining the JER Cryptocurrency genesis block, the challenges I faced, and the technical solutions I developed.
The Challenge
What is a Genesis Block?
Every blockchain starts with a genesis block—block #0, hardcoded into the network's parameters. Unlike regular blocks, the genesis block:
- Has no previous block (prev_hash = 0x00...00)
- Contains a custom message (Satoshi's: "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks")
- Must meet the network's proof-of-work difficulty
- Is permanently referenced by all nodes
Why Proper Difficulty Matters
Bitcoin's genesis block used difficulty 0x1d00ffff, requiring a hash starting with approximately 8 zero bits. This provides:
- Real security: Expensive to compute, expensive to attack
- Fair launch: Everyone starts mining from the same difficulty
- Production-ready: Same security model as Bitcoin from day one
Many altcoins use easy difficulty (like 0x207fffff) for development, but this provides zero security against attacks. For JER, I wanted production-grade security from genesis.
The Technical Stack
Core Technologies
Language: C++17/C++20
Cryptography: OpenSSL 3.x (SHA-256)
Build System: CMake 3.28+
Base: Bitcoin Core v30
Platform: macOS (M-series ARM64)
Genesis Block Parameters
const char* pszTimestamp = "JER 2025 - Empowering global payments";
uint32_t nTime = 1761721936; // Unix timestamp
uint32_t nBits = 0x1d00ffff; // Bitcoin's starting difficulty
uint32_t nNonce = ??? // What we need to findThe goal: Find a nonce value where:
SHA256(SHA256(block_header)) < target
Where target for 0x1d00ffff is:
00000000ffff0000000000000000000000000000000000000000000000000000
Attempt 1: Python Miner (Failed)
The Approach
I started with a Python script using hashlib for SHA-256:
import hashlib
import struct
def mine_genesis(timestamp, bits, message):
target = bits_to_target(bits)
for nonce in range(0xffffffff):
header = build_block_header(timestamp, bits, nonce)
hash_result = hashlib.sha256(hashlib.sha256(header).digest()).digest()
if int.from_bytes(hash_result, 'little') < target:
print(f"Found! Nonce: {nonce}")
return nonce
return NoneThe Result
After 26 minutes, the Python miner found a valid nonce:
Nonce: 2105784136
Hash: 00000000629a5c6e8cb346d2aec702b1b9b40e16cf1750dda0f0b08decee02bd
Success! Or so I thought...
The Problem
When I updated chainparams.cpp with these values and built JER:
genesis = CreateGenesisBlock(pszTimestamp, genesisOutputScript,
1761721935, 2105784136, 0x1d00ffff, 1, 50 * COIN);
assert(consensus.hashGenesisBlock == uint256{"00000000629a5c6e..."});Assertion failed! Bitcoin Core calculated a different hash for the same block.
Root Cause Analysis
The issue: coinbase transaction format mismatch.
Bitcoin Core's CreateGenesisBlock() creates a specific coinbase transaction:
CMutableTransaction txNew;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << nBits << CScriptNum(4)
<< std::vector<unsigned char>((const unsigned char*)pszTimestamp,
(const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].nValue = nSubsidy;
txNew.vout[0].scriptPubKey = genesisOutputScript;My Python script created a similar but not identical transaction structure. The Merkle root differed by a few bytes, causing a completely different block hash.
Key Learning: External miners must replicate Bitcoin Core's exact coinbase format—there's no room for "close enough."
Attempt 2: In-Process Mining (Failed)
The Approach
Since the Python miner's coinbase didn't match, why not mine directly in Bitcoin Core?
I added a mining loop to chainparams.cpp:
// In CMainParams constructor
genesis = CreateGenesisBlock(pszTimestamp_main, genesisOutputScript_main,
1761721935, 0, 0x1d00ffff, 1, 50 * COIN);
// Mining loop
arith_uint256 bnTarget;
bnTarget.SetCompact(genesis.nBits);
printf("Mining mainnet genesis block...\n");
for (uint32_t nNonce = 0; nNonce < 0xffffffff; nNonce++) {
genesis.nNonce = nNonce;
uint256 hash = genesis.GetHash();
if (UintToArith256(hash) <= bnTarget) {
printf("\n=== GENESIS BLOCK FOUND! ===\n");
printf("Nonce: %u\n", nNonce);
printf("Hash: %s\n", hash.ToString().c_str());
break;
}
// Progress every 10 seconds
if (nNonce % 25000000 == 0) {
printf("Progress: %.2f%%\n", nNonce * 100.0 / 0xffffffff);
}
}
consensus.hashGenesisBlock = genesis.GetHash();The Result
Compiled, started bitcoind, watched CPU hit 100%... mining ran beautifully to 99.94% progress, then:
[error] Errors in block header at FlatFilePos(nFile=0, nPos=8) while reading block
[error] A fatal internal error occurred: Failed to read block.
Crash! The node shut down immediately.
Root Cause Analysis
The problem: initialization timing.
The mining loop runs in the CMainParams constructor, which happens during static initialization—before the blockchain database is ready:
1. Static initialization starts
2. CMainParams constructor runs
3. -> Mining loop finds genesis (✓)
4. -> Tries to return from constructor
5. Main() starts
6. Database initialization begins (LevelDB)
7. Node tries to load genesis block
8. -> Database not ready yet!
9. -> Error reading block
10. -> Fatal error, shutdown
Key Learning: Genesis mining must happen before starting the node, not during node initialization.
Attempt 3: Standalone Miner (Success!)
The Solution
Build a standalone C++ program that:
- Uses Bitcoin Core's exact SHA-256 implementation (OpenSSL)
- Replicates the block header format exactly
- Runs independently—no database dependencies
- Outputs the found nonce and hash
The Implementation
// mine-genesis-standalone.cpp
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstring>
#include <ctime>
#include <openssl/sha.h>
// Bitcoin's double SHA256
void double_sha256(const unsigned char* data, size_t len, unsigned char* hash) {
unsigned char temp[32];
SHA256(data, len, temp);
SHA256(temp, 32, hash);
}
// Block header structure (80 bytes)
struct BlockHeader {
uint32_t version; // 4 bytes
unsigned char prev_block[32]; // 32 bytes (all zeros for genesis)
unsigned char merkle_root[32]; // 32 bytes
uint32_t timestamp; // 4 bytes
uint32_t bits; // 4 bytes (difficulty)
uint32_t nonce; // 4 bytes
void serialize(unsigned char* output) const {
memcpy(output, &version, 4);
memcpy(output + 4, prev_block, 32);
memcpy(output + 36, merkle_root, 32);
memcpy(output + 68, ×tamp, 4);
memcpy(output + 72, &bits, 4);
memcpy(output + 76, &nonce, 4);
}
};
// Check if hash meets target
bool check_target(const unsigned char* hash) {
// Target: 00000000ffff0000000000000000000000000000000000000000000000000000
// Hash is little-endian, so check from the end
if (hash[31] != 0x00 || hash[30] != 0x00 ||
hash[29] != 0x00 || hash[28] != 0x00) {
return false;
}
// Next 2 bytes must be <= 0xffff
return (hash[27] <= 0xff);
}
int main() {
BlockHeader header;
header.version = 1;
memset(header.prev_block, 0, 32);
// Merkle root from Bitcoin Core's CreateGenesisBlock()
unsigned char merkle_hex[] = {
0x58, 0x96, 0xf9, 0xa1, 0x9a, 0x70, 0xf5, 0xab,
0x9b, 0xae, 0x12, 0x77, 0xdb, 0x3e, 0xea, 0x5b,
0x80, 0x62, 0x38, 0xe3, 0xbf, 0xf6, 0x48, 0x91,
0xa8, 0xf2, 0xc2, 0xd4, 0xc5, 0x88, 0xda, 0x25
};
memcpy(header.merkle_root, merkle_hex, 32);
header.timestamp = 1761721935;
header.bits = 0x1d00ffff;
uint32_t start_time = time(nullptr);
uint64_t total_hashes = 0;
// Try different timestamps if nonce space exhausted
for (int ts_offset = 0; ts_offset < 100; ts_offset++) {
header.timestamp = 1761721935 + ts_offset;
if (ts_offset > 0) {
std::cout << "\nNonce space exhausted, trying timestamp: "
<< header.timestamp << std::endl;
}
unsigned char block_data[80];
unsigned char hash[32];
for (uint64_t nonce = 0; nonce <= 0xffffffffULL; nonce++) {
header.nonce = (uint32_t)nonce;
header.serialize(block_data);
double_sha256(block_data, 80, hash);
total_hashes++;
if (check_target(hash)) {
uint32_t elapsed = time(nullptr) - start_time;
std::cout << "\n\n========================================\n";
std::cout << "GENESIS BLOCK FOUND!\n";
std::cout << "========================================\n\n";
std::cout << "Timestamp: " << header.timestamp << "\n";
std::cout << "Nonce: " << header.nonce << "\n";
std::cout << "Total time: " << elapsed << " seconds\n";
std::cout << "Total hashes: " << total_hashes << "\n";
std::cout << "Hash rate: " << (total_hashes/elapsed/1000.0) << " KH/s\n";
return 0;
}
// Progress update every 10 seconds
if (nonce % 25000000 == 0) {
uint32_t elapsed = time(nullptr) - start_time;
double hashrate = elapsed > 0 ? (double)total_hashes / elapsed : 0;
std::cout << "Nonce: " << nonce
<< " | Time: " << elapsed << "s"
<< " | Rate: " << (hashrate/1000) << " KH/s"
<< "\r" << std::flush;
}
}
}
std::cout << "\n No valid genesis found after 100 timestamps\n";
return 1;
}Building the Miner
g++ -std=c++17 -O3 -o mine-genesis mine-genesis-standalone.cpp \
-I/opt/homebrew/opt/openssl@3/include \
-L/opt/homebrew/opt/openssl@3/lib \
-lssl -lcryptoThe -O3 flag is crucial—it enables aggressive compiler optimizations, nearly doubling the hash rate.
Running the Miner
$ ./mine-genesis
======================================================================
JER CRYPTOCURRENCY - GENESIS BLOCK MINER
======================================================================
Mining Configuration:
Timestamp: 1761721935
Difficulty: 0x1d00ffff
Target: 00000000ffff0000000000000000000000000000000000000000000000000000
Mining will try multiple timestamps if needed...
Nonce: 25238976 | Time: 10s | Rate: 2523.9 KH/s | Progress: 0.588%
Nonce: 52268548 | Time: 20s | Rate: 2613.4 KH/s | Progress: 1.217%
...
Nonce: 4265304845 | Time: 1580s | Rate: 2699.6 KH/s | Progress: 99.309%
Nonce space exhausted. Trying timestamp: 1761721936
Nonce: 1981842781 | Time: 2091s | Rate: 3001.8 KH/s
========================================
GENESIS BLOCK FOUND!
========================================
Timestamp: 1761721936
Nonce: 1981842781
Total time: 2091 seconds (34.85 minutes)
Total hashes: 6276810078
Hash rate: 3001.8 KH/sSuccess! After 34.85 minutes and 6.28 billion hashes, we found a valid genesis block.
Performance Analysis
Hash Rate Breakdown
Python implementation: ~180 KH/s (interpreted, slow)
C++ without optimization: ~1,500 KH/s (compiled, no -O3)
C++ with -O3 flag: ~3,000 KH/s (optimized)
The -O3 optimization nearly doubled performance by:
- Loop unrolling
- Vectorization (SIMD instructions)
- Inlining functions
- Better branch prediction
Nonce Space Analysis
- Total nonce space: 4,294,967,295 (2^32)
- First timestamp: Exhausted (tried all 4.3B)
- Second timestamp: Found at 1,981,842,781 (46% through)
- Total hashes: 6,276,810,078 (1.46× full nonce space)
Integrating with Bitcoin Core
Updating chainparams.cpp
// patches/chainparams.cpp - MAINNET section
const char* pszTimestamp_main = "JER 2025 - Empowering global payments";
const CScript genesisOutputScript_main = CScript() <<
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb6..."_hex
<< OP_CHECKSIG;
// Properly mined genesis block with Bitcoin difficulty (0x1d00ffff)
// Mined October 29, 2025 - 34.85 minutes @ 3,001 KH/s
genesis = CreateGenesisBlock(pszTimestamp_main, genesisOutputScript_main,
1761721936, // timestamp (adjusted +1)
1981842781, // nonce (found by miner)
0x1d00ffff, // bits
1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
// Verify the hash matches what we mined
assert(consensus.hashGenesisBlock ==
uint256{"00000000e41027343c14981bb43755b77046179e1c658b5fa38ccebf38ce3fcc"});
// Verify the Merkle root is correct
assert(genesis.hashMerkleRoot ==
uint256{"25da88c5d4c2f2a89148f6bfe33862805bea3edb7712ae9babf5709aa1f99658"});Building and Testing
# Sync patches
$ ./scripts/sync-patches.sh
✓ Patches applied
# Rebuild (with new genesis values)
$ ./scripts/build-JER.sh
[100%] Built target bitcoind
✓ Build complete in 54 seconds
# Test mainnet startup
$ bitcoind -daemon
Bitcoin Core starting
$ bitcoin-cli getblockchaininfo
{
"chain": "main",
"blocks": 0,
"headers": 0,
"bestblockhash": "00000000e41027343c14981bb43755b77046179e1c658b5fa38ccebf38ce3fcc",
"difficulty": 1,
"chainwork": "0000000000000000000000000000000000000000000000000000000100010001"
}Perfect! The mainnet starts successfully with the properly mined genesis block.
Lessons Learned
1. Format Compatibility Matters
External tools must replicate Bitcoin Core's exact data structures. "Close enough" doesn't work in cryptography—a single byte difference produces a completely different hash.
2. Timing is Everything
Understanding when code runs during initialization is crucial. Mining during static initialization caused database access before the database was ready.
3. Performance Optimization
Compiler flags matter! -O3 doubled our hash rate from 1,500 to 3,000 KH/s—cutting mining time in half.
4. Iterative Problem Solving
- Attempt 1 taught us about format compatibility
- Attempt 2 taught us about initialization timing
- Attempt 3 combined both lessons into a working solution
5. Proper Tooling
Building a standalone miner was extra work upfront but provided:
- Reusability for other networks
- No risk of corrupting node state
- Clear separation of concerns
Conclusion
Mining a genesis block with proper difficulty isn't just about finding a nonce—it's about understanding:
- The Bitcoin protocol's exact data structures
- C++ initialization and timing
- Cryptographic hash functions and optimization
- Systems programming best practices
The journey from a 2-line Python script to a production C++ miner taught me more about Bitcoin internals than months of reading documentation.
Final Stats:
- 3 attempts: Python → In-process → Standalone
- 2 failures: Format mismatch, timing issue
- 1 success: Standalone C++ miner
- 35 minutes: Total mining time
- 6.28 billion hashes: Computational proof-of-work
Now JER Cryptocurrency has a genesis block with the same security model as Bitcoin.
Resources
- Full Code: https://github.com/frdrckj/JER_Coin
- Genesis Miner:
scripts/mine-genesis-standalone.cpp - Bitcoin Core: https://github.com/bitcoin/bitcoin
- OpenSSL: https://www.openssl.org/
FJerusha