Code Audit: TwistCoin Core vs. Bitcoin Core

Every change TwistCoin Core makes to Bitcoin Core v31.1, what it does, and what reviewing it turned up.

5 October 2026, updated 7 October 2026 - The TwistCoin developers - Reviewed commit 8935cd8

This is an internal review by the project, not an independent third-party audit. It is published so that anyone can check the claims against the code. Corrections and further findings are welcome on GitLab.
Contents
  1. Scope and method
  2. Summary
  3. What was changed
  4. Mainnet parameter review
  5. Findings
  6. Verified as correct

1. Scope and method

The review covers the complete difference between the upstream tag v31.1 of bitcoin/bitcoin and commit 8935cd8 of twistcoin-core: 82 files changed, 392 lines added and 8,552 removed. Most removed lines are Bitcoin's CI configuration and Bitcoin's list of seed nodes. In src/ itself, 24 files change.

How the review was done:

Not done: fuzzing, Bitcoin Core's own unit and functional tests (they had not been adapted to TwistCoin's parameters at the time of the review, see TC-04, since fixed), and any review of the unchanged Bitcoin Core code beyond the paths that use changed values. The pool, explorer and other TwistCoin repositories are out of scope.

2. Summary

The fork is small and conservative. The consensus code itself is untouched except for the block reward, and all other consensus differences are parameter values. Network identity is fully separated from Bitcoin. The review found one critical issue, a missing mainnet parameter that let any peer crash a node. It was fixed and deployed to all three public nodes on 5 October 2026. The remaining findings are inherited weaknesses, maintenance gaps and cosmetic leftovers.

Update, 7 October 2026: Bitcoin Core's test suite has been adapted to TwistCoin and runs on every push (TC-04, fixed), signet is now a TwistCoin network (TC-06, partly fixed), and re-enabling the tests found one more issue in the testnet parameters (TC-11, fixed).
IDFindingSeverityStatus
TC-01Missing mainnet headers-sync parameters allow a remote crashCriticalFixed
TC-02Timewarp protection (BIP94) not enforced on mainnetMediumFollows Bitcoin
TC-03Minimum chain work and assumevalid are zeroLowOpen
TC-04Test suite not adapted, tests and CI disabledLowFixed
TC-05Currency unit still displayed as "BTC"InfoOpen
TC-06Testnet4 and signet were still Bitcoin's networksInfoSignet fixed, testnet4 open
TC-07MAX_MONEY is below the supply capInfoNo action needed
TC-08BIP30 is checked on every blockInfoNo action needed
TC-09Wallet key derivation identical to Bitcoin'sInfoOpen
TC-10New user agent breaks tools that detect "Satoshi"InfoAcknowledged
TC-11Testnet difficulty adjustment could overflowLowFixed

3. What was changed

AreaFilesChange
Chain parameters src/kernel/chainparams.cpp New genesis blocks, block interval, retarget window, halving interval, soft-fork heights, network magic, ports, address prefixes, seeds, headers-sync parameters (see section 4).
Block reward src/validation.cpp GetBlockSubsidy(): initial subsidy 50 → 20 coins. This is the only change to consensus code. The halving logic itself is unchanged.
RPC ports src/chainparamsbase.cpp Default RPC ports 8332 → 8432 (mainnet), and matching changes on the test networks.
Seed nodes src/chainparamsseeds.h, contrib/seeds/nodes_main.txt Bitcoin's mainnet fixed seeds replaced by the three TwistCoin nodes.
Domain separation src/clientversion.cpp, src/common/signmessage.cpp, src/key.cpp, src/common/args.cpp User agent /TwistCoin:31.1.0/, signed-message prefix TwistCoin Signed Message:, key self-test string, config file twistcoin.conf, data directory ~/.twistcoin.
Units and help text src/policy/feerate.h and 13 other files Smallest unit renamed "sat" → "wisp" (CURRENCY_ATOM), plus comments and RPC help strings.
Build and packaging CMakeLists.txt, src/CMakeLists.txt, src/qt/CMakeLists.txt, Dockerfile, .dockerignore, contrib/rename-binaries.sh Product name, twistcoind/twistcoin-cli symlinks next to the unchanged binary names, Docker image.
Removed .github/, ci/ Bitcoin Core's CI pipelines and scripts.
Documentation README.md, CONTRIBUTING.md, SECURITY.md, doc/README.md Rewritten for TwistCoin.

4. Mainnet parameter review

ParameterBitcoinTwistCoinAssessment
nPowTargetSpacing600 s120 sOK.
nPowTargetTimespan1,209,600 s17,280 sOK: gives a 144-block retarget interval (timespan / spacing). The 4x clamp still applies.
nSubsidyHalvingInterval210,000525,600OK: about 2 years at 120 s per block.
powLimit0x1d00ffff0x1e0ffff0OK: easier than the genesis target, so it is never the binding limit at launch. Allows recovery if hashrate collapses.
BIP34/65/66, CSV, SegWit heightshistorical heights0OK: active from genesis. See TC-08 for one side effect.
Taproot deploymentBIP9, 2021ALWAYS_ACTIVEOK.
script_flag_exceptions2 Bitcoin blocksnoneOK: they named Bitcoin block hashes that cannot exist on TwistCoin.
enforce_BIP94falsefalseInherited weakness, see TC-02.
nMinimumChainWork, defaultAssumeValidrecent values0See TC-03.
pchMessageStartf9 be b4 d97f 87 c0 69OK: unique to TwistCoin.
P2P / RPC port8333 / 83328433 / 8432OK.
Address prefixesbc, 0, 5, 128tw, 65, 63, 193OK: tw1..., T..., S.... The WIF byte follows the usual "address byte + 128" convention.
BIP32 version bytesxpub/xprvTpub/TprvOK on mainnet. See TC-09 for derivation paths.
Genesis block20092026-08-15OK: hash and merkle root are asserted at startup, and the coinbase script is under the 100-byte limit.
vSeeds, vFixedSeedsBitcoin seeders3 TwistCoin nodesOK.
m_assumeutxo_data, chainTxDataBitcoin snapshotsempty / 0OK: there is no history to snapshot yet.
m_headers_sync_params641 / 15218209 / 5096 (since 8935cd8)Was missing, see TC-01.

5. Findings

TC-01 - Missing mainnet headers-sync parameters allow a remote crash Critical Fixed

Location
src/kernel/chainparams.cpp (CMainParams)
Fixed in
8935cd8, deployed to all three public nodes on 2026-10-05

When the Bitcoin-specific values were removed from the mainnet parameters, the m_headers_sync_params block went with them, leaving both of its fields at zero. The test networks kept theirs. Bitcoin Core uses these values when a peer offers a chain of headers with less total work than the node's anti-DoS threshold (its protection against memory exhaustion by cheap header chains). The constructor of that protection asserts that the values are non-zero, and Bitcoin Core is always built with assertions enabled. A peer able to reach that code path could therefore stop any TwistCoin mainnet node.

Fix: the parameters were restored, with values computed by Bitcoin Core's own tool, contrib/devtools/headerssync-params.py, using TwistCoin's 2-minute block interval and genesis date. This is not a consensus change: patched and unpatched nodes remain fully compatible. Anyone running TwistCoin Core built from an earlier commit should upgrade.

TC-02 - Timewarp protection (BIP94) not enforced on mainnet Medium

Location
src/kernel/chainparams.cpp: consensus.enforce_BIP94 = false
Status
Follows Bitcoin (see the governance policy)

The difficulty adjustment inherits Bitcoin's timewarp weakness: a miner with a majority of the hashrate can manipulate timestamps at retarget boundaries to drive the difficulty down and issue coins faster than scheduled. Bitcoin Core already contains the BIP94 mitigation (used on testnet4), but it is off on Bitcoin mainnet, and therefore on TwistCoin mainnet as well. The weakness is the same as Bitcoin's. The practical risk is higher on TwistCoin, because a majority of TwistCoin's hashrate is cheap to obtain.

Recommendation: in line with the project's policy of following Bitcoin's consensus decisions, adopt a timewarp fix when Bitcoin activates one. Enabling BIP94 on TwistCoin alone would be a soft fork that Bitcoin has not made.

TC-03 - Minimum chain work and assumevalid are zero Low

Location
src/kernel/chainparams.cpp: nMinimumChainWork, defaultAssumeValid
Status
Open

Both values are empty, which is normal for a chain at launch. With nMinimumChainWork = 0, though, a freshly started node has no work threshold during initial sync. It can be led to store low-work header chains and follow a dishonest peer's chain until it hears about the real one. The cost of zero assumevalid (all scripts verified) is negligible at the current chain size.

Recommendation: set both values to a recent block at each release, as Bitcoin Core does, and regenerate m_headers_sync_params at the same time.

TC-04 - Test suite not adapted, tests and CI disabled Low Fixed

Location
src/test/, test/functional/, build flag -DBUILD_TESTS=OFF, removed ci/ and .github/
Fixed in
b71051f, 10557ba and fd4b11e (7 October 2026)

No test file was changed. At least 32 test files still assume Bitcoin's parameters: Bitcoin's genesis hash, bcrt1 addresses, regtest's 150-block halving, or a 50-coin subsidy (for example src/test/validation_tests.cpp). All builds disable the tests, and the CI was removed. As a result, none of the changed consensus parameters is covered by an automated test, and a regression would only be noticed in production.

Recommendation: start with the unit tests that cover the changed code (subsidy, chain parameters, difficulty), keep a build that runs them, and adapt the functional tests progressively.

Fix: the whole test suite was adapted to TwistCoin's parameters rather than the other way round (regtest keeps TwistCoin's 20-coin subsidy and halving interval). All unit tests and all functional tests now pass, apart from those that need tools not included in the build, which are skipped as in Bitcoin Core. A GitLab CI pipeline builds the node and runs both suites on every push. Where tests relied on Bitcoin data, it was regenerated for TwistCoin: the difficulty adjustment tests use real TwistCoin retargets, and the mainnet mining test replays a full 144-block retarget period of real TwistCoin mainnet proof-of-work, mined for the purpose by an ASIC.

TC-05 - Currency unit still displayed as "BTC" Info

Location
src/policy/feerate.h: CURRENCY_UNIT = "BTC"
Status
Open

The smallest unit was renamed to "wisp", but the main unit is still "BTC" in RPC help text and formatted amounts (for example BTC/kvB fee rates). This has no effect on behaviour, but can confuse users.

TC-06 - Testnet4 and signet were still Bitcoin's networks Info

Location
src/kernel/chainparams.cpp: CTestNet4Params, SigNetParams
Status
Signet fixed in 89485a2 (7 October 2026), testnet4 open

These networks were left as they are in Bitcoin, apart from having their seed nodes removed. Testnet4 keeps Bitcoin's genesis, port and tb prefix. Signet keeps Bitcoin's genesis, default challenge, port and network magic. A TwistCoin node started with -signet and pointed at a peer would therefore join Bitcoin's signet. Neither network is deployed for TwistCoin.

Recommendation: either give them TwistCoin genesis blocks and identities, or refuse to start on them.

Partial fix: signet is now a TwistCoin network, with its own genesis block, tws address prefix, port 38633 and TwistCoin pacing (2-minute blocks, 144-block retarget). Its default challenge is a key held by the project, which signs the blocks of the default signet; anyone can still run their own signet with -signetchallenge. Testnet4 is unchanged and still not deployed.

TC-07 - MAX_MONEY is below the supply cap Info

Location
src/consensus/amount.h: MAX_MONEY = 21000000 * COIN
Status
No action needed

The total supply will eventually reach about 21,024,000 TWIST, slightly above MAX_MONEY. This constant is only a sanity bound on individual amounts (a single output, the outputs of one transaction, the fees of one block). It is never compared to the total supply. In practice, it only means that one transaction cannot move more than 21 million TWIST. Changing it would be a consensus change.

TC-08 - BIP30 is checked on every block Info

Location
src/kernel/chainparams.cpp: BIP34Height = 0, BIP34Hash unset; used in src/validation.cpp (ConnectBlock)
Status
No action needed

Bitcoin skips the BIP30 duplicate-transaction lookup once BIP34 is known to be active, by comparing the block at BIP34Height with BIP34Hash. With the hash left empty, the comparison never matches, so TwistCoin always runs the BIP30 check. This is safe, since it is the stricter path, at a small extra validation cost.

TC-09 - Wallet key derivation identical to Bitcoin's Info

Location
src/wallet/walletutil.cpp; test-network BIP32 version bytes in chainparams.cpp
Status
Open

The built-in wallet derives mainnet keys with coin type 0, as Bitcoin does (for example m/84h/0h/0h), and the test networks reuse Bitcoin testnet's extended key version bytes. A seed used in both a Bitcoin wallet and a TwistCoin wallet therefore produces the same private keys on both chains, which links the two wallets.

Recommendation: document that a Bitcoin seed should never be reused for TwistCoin. A distinct coin type could be adopted at the wallet level (not a consensus change), but it would need a migration for existing wallets.

TC-10 - New user agent breaks tools that detect "Satoshi" Info

Location
src/clientversion.cpp: UA_NAME = "TwistCoin"
Status
Acknowledged

Some third-party software identifies Bitcoin Core by its /Satoshi: user agent. The Fulcrum Electrum server, for instance, otherwise assumes it is talking to a Bitcoin Cash node and uses RPC methods TwistCoin Core does not have. TwistCoin keeps its own user agent and ships a patched Fulcrum (twistcoin-fulcrum) instead. Other tools may need the same treatment.

TC-11 - Testnet difficulty adjustment could overflow Low Fixed

Location
src/kernel/chainparams.cpp: CTestNetParams
Fixed in
89485a2 (7 October 2026), found by the re-enabled unit tests

The fork relaxed testnet's powLimit but kept Bitcoin's two-week retarget timespan. CalculateNextWorkRequired() multiplies the previous target by the actual timespan, up to four times the target timespan, so with these values the intermediate result could exceed 256 bits and wrap around, producing a wrong difficulty. Bitcoin Core's own chain parameter sanity test caught it as soon as the unit tests were running again. Testnet is not deployed, and mainnet was checked: its margin is about 2252 against the 2256 limit.

Fix: testnet now uses mainnet's pacing (2-minute blocks, 144-block retarget), like regtest and signet.

6. Verified as correct