SPEARMINT SPMTC

Spearmint/Articles/Fork Bitcoin Core v29

Engineering · Bitcoin Core · chain parameters

How to fork Bitcoin Core v29: chainparams, genesis block, and what breaks

A fork of Bitcoin Core is a build, a handful of edits to kernel/chainparams.cpp, three mined genesis blocks, and a long tail of things that silently assume you are still Bitcoin. Here is the sequence, using the commands Spearmint actually ran — including the one that failed.

By , Instant Access · Published 13 September 2026 · Updated 13 September 2026 · ~11 min read

01 What a fork actually is

Bitcoin Core does not know it is running Bitcoin. It knows a set of parameters: four magic bytes that prefix every network message, a port, a genesis block hash, a difficulty floor, a bech32 prefix, and a list of seed nodes. Change those and the same binary refuses to talk to Bitcoin nodes, refuses Bitcoin blocks, and starts a chain of its own. That is the whole of a “code fork” in the sense used here: a new network running Bitcoin's consensus rules, not a chain split that shares Bitcoin's history.

The consequence worth stating at the outset is that a fork inherits Bitcoin Core's review, and only that. The code paths are the most examined in the industry. The parameters you type, and any rule you change, are examined by nobody until you publish them. Spearmint's position is that novelty belongs in parameters and in a small, published set of consensus additions, and nowhere else — the overview sets out why.

02 Build v29 first, unchanged

Bitcoin Core moved its build system from Autotools to CMake in v29.0, so older fork tutorials that begin with ./autogen.sh no longer apply. Build the unmodified tag before touching anything, so that any later failure is unambiguously yours. These are the commands from the Spearmint build log, run on Ubuntu 22.04 with the wallet enabled:

# dependencies sudo apt-get update sudo apt-get install -y build-essential cmake pkg-config libevent-dev \ libboost-all-dev libsqlite3-dev # source, pinned to the release tag git clone https://github.com/bitcoin/bitcoin.git cd bitcoin git fetch --tags git checkout v29.0 # configure and build cmake -B build cmake --build build -j"$(nproc)" # install the daemon only sudo cmake --install build --component bitcoind ./build/bin/bitcoind -version

Note the last line. cmake --install installs per component, so bitcoin-cli, bitcoin-wallet, bitcoin-tx and bitcoin-util each need their own --component invocation. The build log records exactly that omission and the four commands that fixed it.

03 Scaffold the fork

Copy the tree, drop the upstream history, and make an initial commit that says what it is. Keeping the upstream git history is a defensible choice too — it makes rebasing onto v30 easier — but a clean root commit makes the diff between “Bitcoin Core v29.0” and “your chain” a single command to produce, which matters when someone asks what you changed.

cd ~ cp -r bitcoin spearmint cd spearmint rm -rf .git git init git add . git commit -m "Spearmint: forked from Bitcoin Core v29.0"

Branding comes next and is deliberately shallow. Rename the CMake project and the output binaries; leave the internal target names alone so that upstream patches still apply:

# top-level CMakeLists.txt project(SpearmintCore LANGUAGES C CXX) # src/CMakeLists.txt — after each add_executable(...) set_target_properties(bitcoind PROPERTIES OUTPUT_NAME "spearmintd") set_target_properties(bitcoin-cli PROPERTIES OUTPUT_NAME "spearmint-cli") set_target_properties(bitcoin-wallet PROPERTIES OUTPUT_NAME "spearmint-wallet") set_target_properties(bitcoin-tx PROPERTIES OUTPUT_NAME "spearmint-tx")

Resist the temptation to search-and-replace “bitcoin” across the tree at this stage. It touches thousands of files, breaks the functional test framework, and buries your real changes in noise. Do it last, if at all.

04 The fields in chainparams.cpp

In v29 the network definitions live in src/kernel/chainparams.cpp (older tutorials say src/chainparams.cpp; that file is now a thin wrapper). There is one class per network — CMainParams, CTestNetParams, CTestNet4Params, SigNetParams, CRegTestParams — and each constructor sets the same fields. These are the ones a fork must change, with Bitcoin's mainnet values as fetched from the v29.0 tag:

kernel/chainparams.cpp — CMainParams fields that define a network
FieldBitcoin v29.0 mainnetWhat it does / Spearmint
pchMessageStart[0..3]f9 be b4 d9Magic bytes on every P2P message. Spearmint: fa bf b0 dc (test …dd, regtest …de).
nDefaultPort8333P2P port. Spearmint: 9333 / 19333 / 19444.
bech32_hrp"bc"Address prefix. Spearmint: spmtc / tsmc / smcrt.
base58Prefixes[…]0, 5, 128, xpub/xprv bytesLegacy address and key prefixes. Change even if you only issue bech32 addresses, or Bitcoin WIF keys will import.
genesis = CreateGenesisBlock(nTime, nNonce, nBits, nVersion, reward)(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN)Your mined values go here, plus a new pszTimestamp string.
consensus.hashGenesisBlock + the two assertsthe 000000000019d6… hashMust equal the hash of the block the constructor builds, or the binary aborts on start.
consensus.powLimit00000000ffff…Easiest allowed target. Keep Bitcoin's on main; regtest uses 7fffff….
consensus.nPowTargetSpacing600Block interval in seconds. Spearmint: 30.
consensus.nPowTargetTimespan1209600Retarget window (14 days). Only meaningful if you keep Bitcoin's algorithm — see the retargeting article.
consensus.nSubsidyHalvingInterval210000Blocks per halving. Spearmint's schedule is not a single interval (six-monthly for five years, then annual), so GetBlockSubsidy() in validation.cpp changes too.
consensus.BIP34Height, BIP65Height, BIP66Height, CSVHeight, SegwitHeight, MinBIP9WarningHeight227931 … 483840Set all to 0 or 1: your chain has these rules from the start. Also clear script_flag_exceptions.
consensus.vDeployments[TAPROOT]activated 2021Set to always-active from genesis unless you want a second activation drama.
consensus.nMinimumChainWorka 256-bit work valueSet to 0 at launch; raise it in later releases to pin history. Spearmint plans to pin the 8% acquisition range this way (transparency).
consensus.defaultAssumeValida recent block hashSet to null (uint256{}) or the node will skip script checks up to a Bitcoin block that does not exist on your chain.
checkpointData13 Bitcoin checkpointsEmpty the map. Add your own only once you have blocks to point at.
m_assumeutxo_data2 snapshotsEmpty it. These are Bitcoin UTXO-set hashes.
chainTxDatatx count, timestamp, rateUsed for the sync-progress estimate only. Set to zeros.
vSeeds, vFixedSeeds9 DNS seeds, seeds headerClear both. Bitcoin's seeds will hand you Bitcoin peers who will ban you for bad magic. Regenerate chainparamsseeds.h from contrib/seeds when you have nodes.
nPruneAfterHeight, m_assumed_blockchain_size, m_assumed_chain_state_size100000, 720, 14Cosmetic. Set the sizes to 1.

Two files outside chainparams.cpp also carry identity. src/chainparamsbase.cpp sets the RPC ports (8332 on Bitcoin mainnet) and data-directory subfolders; change the ports so a fork and a Bitcoin node can share a host. And src/validation.cpp holds GetBlockSubsidy(), which is where a non-standard emission schedule such as Spearmint's lives.

05 Mining the genesis blocks

A genesis block is an ordinary block with no predecessor: a coinbase transaction whose input script carries a text string, a single output paying a public key, and a header that has to satisfy the network's difficulty floor. Nothing in Core mines it for you. You compute the merkle root from the coinbase, then loop over nNonce (and nTime if the nonce wraps) until the double-SHA-256 of the 80-byte header is below the target encoded by nBits.

At Bitcoin's floor of 0x1d00ffff that is on average about 232 hashes — roughly 4.3 billion — which a Python script does in minutes to hours depending on the machine, and which a C implementation does in seconds. You need three: main, test and regtest each have their own timestamp string, and regtest's powLimit is so easy that its block is found almost instantly. Spearmint's strings are published in the build log:

  • Main: “Spearmint 25/Oct/2025: From code we trust, not fiat we must.”
  • Test: “Spearmint Testnet 25/Oct/2025: Trial by code, not fiat.”
  • Regtest: “Spearmint Regtest 25/Oct/2025: Proof in code, not paper.”

The genesis output's public key is conventionally an uncompressed key — 65 bytes, 130 hex characters — whose private key is discarded, because the genesis coinbase is unspendable in Bitcoin Core regardless (it is never added to the UTXO set). Once mined, the values go into CreateGenesisBlock(...) and the two assert lines that follow it are updated with the new block hash and merkle root. Get either wrong and the daemon aborts before it opens a socket, which is the intended behaviour.

06 A real failure: odd-length string

Spearmint automated the three genesis mines and the chainparams.cpp patch in one script, contrib/spearmint/spearmintify.sh. Its first run failed:

# while mining genesis binascii.Error: Odd-length string

The cause was mundane and worth recording precisely because it is the kind of thing that never appears in a tutorial. The script had been assembled from a heredoc, and the heredoc was truncated, which cut the 130-character genesis_pubkey to an odd number of hex digits. binascii.unhexlify cannot decode an odd-length string, so the miner aborted before producing a single block. The fix was to write the miner as a clean file with the full key and run the wrapper again:

cd ~/spearmint cat > contrib/spearmint/genesis_miner.py <<'PY' # clean script written — includes the full 130-hex pubkey PY chmod +x contrib/spearmint/genesis_miner.py bash contrib/spearmint/spearmintify.sh
Why it is published A build log that contains only successes is a brochure. The Spearmint log keeps the traceback so the bootstrap can be audited as it happened rather than reconstructed from a tidied history.

07 What breaks after the rename

Most of the effort in a fork is not the edits above; it is the long list of places that assume the parameters have Bitcoin's values. In rough order of how soon each one bites:

Things that assume you are still Bitcoin
SymptomCauseFix
Daemon aborts on start with an assertionhashGenesisBlock or the merkle-root assert does not match the block the constructor buildsRe-check the mined values; the nTime/nNonce/nBits tuple and the timestamp string must all match.
Node connects to Bitcoin peers and is bannedSeeds still point at BitcoinClear vSeeds/vFixedSeeds; run with -connect= to your own nodes until you have seeds.
Blocks validate suspiciously fast, or a later block is rejected as invaliddefaultAssumeValid or checkpointData still hold Bitcoin hashesNull them. Reintroduce checkpoints only for your own chain.
Wrong coins per block, or a halving at height 210,000GetBlockSubsidy() and nSubsidyHalvingInterval unchangedImplement your schedule in validation.cpp and write a unit test for the total supply.
Chain stalls after a miner leaves, or difficulty never movesBitcoin's 2,016-block retarget on a chain with a few minersReplace the algorithm in pow.cpp (next article).
Functional tests fail everywheretest/functional hard-codes ports, regtest genesis, subsidy and the binary namesUpdate test_framework/util.py port bases and the regtest parameters; expect a day of test triage.
GUI, man pages and help text say “Bitcoin”Strings and PACKAGE_NAMECosmetic; do it last and in one commit.
Wallets import Bitcoin keys, or your addresses validate on Bitcoin toolsbase58Prefixes left at Bitcoin's valuesChange all five prefixes even if you only use bech32.
Signet/testnet4 code paths reference Bitcoin's signet challengeSigNetParams unchangedEither define your own signet or disable the chain type.

There is no substitute for booting regtest first. Spearmint's build log lists exactly that as the next step after the genesis patch: regtest, then a private mainnet on the new chain IDs, then ckpool in solo mode against getblocktemplate — the same sequence the node page describes for anyone running the software independently.

08 Consensus changes are the risky part

Everything above is configuration. It can be wrong in ways that stop the node from starting, which is embarrassing and cheap. Consensus changes are different: they can be wrong in ways that a node accepts happily for a year and then splits the network, and they are the only part of a fork that Bitcoin Core's reviewers have never seen.

Spearmint makes two. Difficulty retargets every block instead of every 2,016 (ADR), and confirmation depth scales with transaction value (DBF). Both are documented on the overview, and both are treated as the highest-risk code in the project: the September 2026 meeting put AI-assisted review of exactly this logic at the top of the threat model, and the testing programme now includes fuzzing and adversarial simulation of the retarget response before any endpoint is public.

A fork is not a secure coin A new SHA-256 chain starts with a tiny fraction of the hashrate that already exists on Bitcoin, on hardware that can be rented by the hour. The code being Bitcoin Core's does not change that arithmetic. Difficulty adjustment, checkpoints and minimum chain work are the tools; none of them is a guarantee, and the October 2025 notes record the residual risk plainly.

09 Key takeaways

  • Bitcoin Core v29 builds with CMake, not Autotools; install each tool with its own --component.
  • Network identity lives in src/kernel/chainparams.cpp: magic bytes, port, bech32 HRP, base58 prefixes, genesis, powLimit, spacing, seeds, checkpoints, assumevalid.
  • Mine three genesis blocks (main, test, regtest); at 0x1d00ffff expect ~4.3 billion hashes for main.
  • Null defaultAssumeValid, empty checkpointData and m_assumeutxo_data, clear the seeds, or your node will trust Bitcoin's history.
  • A non-standard emission schedule means editing GetBlockSubsidy() in validation.cpp, not just the halving interval.
  • The configuration is cheap to get wrong; the consensus changes are not. Test them as if they were hostile.

10 Questions

Where is chainparams.cpp in Bitcoin Core v29?
In src/kernel/chainparams.cpp. Older tutorials refer to src/chainparams.cpp, which is now a thin wrapper. RPC ports and data-directory names are in src/chainparamsbase.cpp; hard-coded seed addresses are generated into src/chainparamsseeds.h from contrib/seeds.
How long does it take to mine a genesis block?
At Bitcoin's minimum difficulty (nBits 0x1d00ffff) about 4.3 billion hashes on average, which a Python script finds in minutes to a few hours and a C loop in seconds. Regtest's powLimit is far easier and is found almost immediately. You need one block per network.
Does forking Bitcoin Core make a secure coin?
No. The codebase is as reviewed as Bitcoin Core's, but security comes from hashrate, and a new SHA-256 chain has almost none relative to the hardware that can be pointed at it. Every consensus change you add is unreviewed code in the most expensive place to be wrong.
Can I keep Bitcoin's difficulty adjustment?
You can, but the 2,016-block window with a 4× clamp assumes hashrate that moves slowly. On a small chain that shares ASICs with Bitcoin, hashrate can move by orders of magnitude in minutes, stalling the chain or making reorgs cheap. Most forks replace it; the retargeting article compares the options.

11 Sources

  1. Bitcoin Core v29.0, src/kernel/chainparams.cppgithub.com/bitcoin/bitcoin/blob/v29.0/src/kernel/chainparams.cpp (field names and mainnet values quoted above).
  2. Bitcoin Core v29.0, src/pow.cppgithub.com/bitcoin/bitcoin/blob/v29.0/src/pow.cpp (GetNextWorkRequired, CalculateNextWorkRequired).
  3. Bitcoin Core v29.0 release notes (CMake build system) — github.com/bitcoin/bitcoin/blob/v29.0/doc/release-notes.md.
  4. Spearmint build log — spearmintcoin.com/progress.html (commands, chain identity, genesis-script failure).
  5. Spearmint economic policy — spearmintcoin.com/economic-policy.html (emission schedule referenced in section 04).