C-1..C-4 — Supply Cap & Halving Schedule

Verifies that b3chain inherits Bitcoin's 21M supply cap, that the block subsidy halves at every 210 000 blocks (mainnet) / 150 blocks (regtest), and that subsidy returns exactly 0 once 64 halvings have elapsed (avoiding undefined right-shift behaviour).

Script: audit-supply-cap.py Runtime: ~12 s Status: 9 / 9 PASS

1. What is being audited

IDInvariant
C-1Block subsidy halves at every 210 000 blocks (mainnet)
C-2Sum of all subsidies converges to 20 999 999.97690000 B3C
C-3Subsidy returns exactly 0 once halvings ≥ 64
C-4Difficulty retarget enforces the 4× lower / upper bounds

2. Why it matters

The 21M cap is the most-quoted property of Bitcoin. If a single line of GetBlockSubsidy() in src/validation.cpp is miscoded, the whole supply schedule breaks — and unlike a UI bug you can never undo it once anyone has used the chain. The audit is short, but it is the kind of bug that has bitten other forks (and one that an external auditor will absolutely check first), so we test it with both an analytic argument and live mining on regtest across four halvings.

3. The math

The subsidy at height h on a chain with halving interval I is:

subsidy(h) = (50 * COIN) >> floor(h / I)

Total supply is the geometric series:

S = I * sum_{n=0}^{63} (50 * COIN) >> n
  = 50 * COIN * I * (2 - 2^-63)
  ≈ 21 000 000 * COIN

For mainnet (I = 210 000) the exact value is 2 099 999 997 690 000 satoshi (= 20 999 999.97690000 B3C).

4. How to run

cd b3chain
python3 contrib/testing/audit/audit-supply-cap.py

5. Expected output

[C-1..C-4] Supply cap and halving schedule
========================================================================
  PASS  [C-2] geometric sum at mainnet interval (210000) equals 20999999.97690000
  PASS  [C-3] subsidy returns 0 at halving 64
  PASS  [C-4] CalculateNextWorkRequired retains the 4x retarget bounds

  Spawning regtest node and mining 600 blocks (4 halvings)...
  PASS  [C-1] subsidy at height 1 (halving 0) = 50.00000000 B3C
  PASS  [C-1] subsidy at height 151 (halving 1) = 25.00000000 B3C
  PASS  [C-1] subsidy at height 301 (halving 2) = 12.50000000 B3C
  PASS  [C-1] subsidy at height 451 (halving 3) = 6.25000000 B3C
  PASS  [C-2] cumulative subsidy through height 600: 14015.62500000 B3C (matches analytic sum)
  PASS  [C-1] subsidy halving schedule observed across 4 halvings on regtest
------------------------------------------------------------------------
  9/9 checks passed in 11.7s
AUDIT RESULT: PASS  [C-1..C-4]

6. Common pitfalls

  • Right-shift ≥ 64. In C/C++, x >> 64 on a 64-bit type is undefined behaviour. The Bitcoin Core implementation guards this with if (halvings >= 64) return 0;. Removing the guard looks innocuous and is a classic fork-killer.
  • Off-by-one halving boundaries. The first block paying the new (halved) subsidy is at exactly I, not I − 1. The audit samples block I + 1 for each halving epoch to catch this.
  • Coinbase fees vs subsidy. Block reward = subsidy + fees. The audit only mines empty blocks, so coinbase value equals the subsidy exactly.

7. Source files

The problem in one sentence

Once a chain emits more than its declared supply, you cannot un-issue the excess; this is the single most reputation-defining bug a fork can ship.

The theory

Bitcoin's supply formula is a geometric series:

\[ S = I \cdot \sum_{n=0}^{63} \lfloor (50 \cdot 10^8) / 2^n \rfloor \]

where I is the halving interval (210000 blocks on mainnet) and the sum is truncated at n = 63 because the C++ expression (50 * COIN) >> 64 on a 64-bit type is undefined behaviour. The guard if (halvings >= 64) return 0; is not optional; it changes the answer on most compilers.

For B3Chain, all parameters are inherited unchanged. The cap evaluates to 2099999997690000 satoshi exactly = 20999999.97690000 B3C.

Hands-on demo

python3 contrib/testing/audit/audit-supply-cap.py

Expected output:

[C-1..C-4] Supply cap and halving schedule
========================================================================
  PASS  [C-2] geometric sum at mainnet interval (210000) equals 20999999.97690000
  PASS  [C-3] subsidy returns 0 at halving 64
  PASS  [C-4] CalculateNextWorkRequired retains the 4x retarget bounds
  Spawning regtest node and mining 600 blocks (4 halvings)...
  PASS  [C-1] subsidy at height 1 (halving 0) = 50.00000000 B3C
  PASS  [C-1] subsidy at height 151 (halving 1) = 25.00000000 B3C
  ...
AUDIT RESULT: PASS  [C-1..C-4]

Exercise

Open src/validation.cpp and locate GetBlockSubsidy(). Remove the >= 64 guard, rebuild, re-run the audit. You should see C-3 fail with a non-zero subsidy at high heights.

// Before
if (halvings >= 64) return 0;
// After (introduces UB)
// (deleted)

Expected new output (compiler-dependent — gcc tends to wrap, clang often optimises to 0 anyway):

  FAIL  [C-3] subsidy at halving 64 returned 50.00000000 B3C, expected 0
AUDIT RESULT: FAIL  [C-1..C-4]

This is exactly the kind of "looks like it works in your tests" bug that has bitten other forks. The audit catches it.

Further reading

  • Bitcoin Core PR for the original guard:

github.com/bitcoin/bitcoin/blob/master/src/validation.cpp

  • Bitcoin Talk thread on the geometric-series cap (Satoshi 2010):

bitcointalk.org/index.php?topic=583.0

  • C++ standard on undefined right-shift (ISO/IEC 14882:2017 §8.7):

open-std.org/jtc1/sc22/wg21/