CHIP-2025-03 Faster Blocks for Bitcoin Cash

Thanks for all the work you’ve been pouring into this CHIP @bitcoincashautist!

I’m strongly in favor, and I hope to have my own software ready for a 2027 upgrade tempnet in the next week or two. :rocket:

Nine-block overpayment at halving boundaries

There’s an issue in the halving calculation that should be corrected – in Block Subsidy:

// Subsidy is cut in half every 126,000,000 ticks which will occur approximately every 4 years.
SUBSIDY_HALVING_INTERVAL = 126'000'000 // ticks equivalent to 210,000 10-minute target intervals
int GetBlockSubsidy(int tick, int targetBlockTime) {
    int halvings = tick / SUBSIDY_HALVING_INTERVAL;
    // Base subsidy: 50 BCH = 5,000,000,000 satoshis
    Amount subsidy = 5'000'000'000 * targetBlockTime / 600; // Scale by target block time expressed in ticks
    // Subsidy is cut in half every 126,000,000 ticks which will occur
    // approximately every 4 years.
    return (subsidy >> halvings);
}

This divides each existing ten-minute subsidy into 10 one-minute subsidies. However, the next halving is “too late” for nine of the ten one-minute blocks that replace the halving block.

For example, with today’s schedule, the next halving occurs at ten-minute height 1,050,000. Block 1,049,999 will have a subsidy of 312,500,000 satoshis, while the halving block itself will have a subsidy of 156,250,000 satoshis.

With the CHIP’s current code, the 20 one-minute blocks replacing ten-minute heights 1,049,999 and 1,050,000 would have subsidies of: 19 blocks of 31,250,000 and the 1 halving block of 15,625,000. That means 9 of the 10 one-minute blocks which replace ten-minute height 1,050,000 will continue to pay the higher subsidy of the unhalved block – only the last one-minute block correctly adjusts down to the lower subsidy. In practice, that permits 1.40625 BCH more subsidy across those 20 one-minute blocks than the two corresponding original payments. (2.8125 BCH across the remaining schedule, net change of 2.5626 BCH after subtracting the 0.2499 BCH integer-division shortfall already mentioned in the CHIP.)

To avoid that additional inflation, we should simply assign installments to the corresponding ten-minute payment before calculating the one-minute subsidy:

-    int halvings = tick / SUBSIDY_HALVING_INTERVAL;
+    int halvings = (tick + 600 - targetBlockTime) / SUBSIDY_HALVING_INTERVAL;

With that correction, the next halving correctly pays 10 one-minute blocks at 31,250,000 equivalent to ten-minute height 1,049,999 and 10 one-minute blocks at 15,625,000 equivalent to ten-minute height 1,050,000.

Note that this solution still “rounds down” some fractional satoshis that would otherwise be issued in the future (those fractional satoshis might ultimately be issued if BCH adds support for fractional satoshis). The first down rounding happens at ten-minute height 1,890,000, approx. ~2044.

It would also be possible to modulate the subsidy to more precisely match the ten-minute schedule – e.g. ten-minute height 5,670,000’s 37-satoshi subsidy would be paid out as 3, 4, 4, 3, 4, 4, 3, 4, 4, 4 over every 10 blocks. That’s harder to reason about and would mean that blocks don’t have a consistent subsidy between halvings, which seems like a footgun for ecosystem software vs. today’s consistent subsidies. I think it’s much preferable to either a) do the above and round all the payments down to 3, meaning that 7 fewer satoshis are minted per 10 blocks, or b) wait for BCH to support fractional satoshis, allowing the calculation to pay an even 3.7 for every block. (And of course, in practice, the goal is for BCH security to be primarily funded by transaction fees long before single-satoshi rounding differences make up a non-trivial percentage of the subsidy.)

9 Likes

Much appreciated, thank you for your support and I’m excited about that tempnet!

Nice catch, and I happened to catch it today, too, while working on tests, literally just ~50 min. before your post landed!

Incredible synchronicity, and this is a good reminder of why tests are important! :slight_smile:

Re. other points

Modulation crossed my mind and I reached the same conclusion as you, can add a little paragraph to rationale.

And current rationale argues for truncation similar to how you do, and notes that fractional sats are a valid mitigation option, nice that we reached the same conclusion!

4 Likes

this is a great next step, I will start testing chained covenants immediately and will be very appreciative of the speed increase!

I’ll also add this 1 min blocks are a no-brainer IMO, so long as they hold up against the adversarial reviews / red teaming. let’s turn some grey hats loose and see what happens

6 Likes

Thanks, this is excellent, and an exact solution to the huge question of security and confirmations using a hard economic analysis instead of everyone rolling their own based on gut feel. Its impressive that we can completely eliminate confirmations. I admit that I don’t fully understand every detail of it of the analysis, but just having this worked out with a formula solves a longstanding problem.

You mention calibrating, I hope we don’t need a price oracle and can simply calibrate hash rate to BCH or something like that. We don’t have to necessarily work it all out today, but this analysis itself and formula for how many ticks to secure a BCH is just so valuable. It could perhaps be used programmatically so that smaller transactions validate faster. And when reviewing code, whenever we see hard coded stuff we can say to do it this way instead.

So thank you for getting this analysis and its conceptual framework started.

AI-assisted summary paragraph:

The real value here is a safe default, not an optimal policy. A rule computable from deposit value alone — no oracles, no hashpower assumptions, invariant under block-time changes — removes the entire class of “hardcoded confirmation count” bugs that would otherwise landmine future cadence changes. Those who understand the minority-vs-majority trade-offs can still do better, but ecosystem safety is set by defaults, not experts. This gives us a default that’s safe by construction, and with the formula written down, bad assumptions in code are no longer excusable.

1 Like

One thing that’s really cool about this: as the block subsidy declines over the halvings, this ratio captures it automatically — the required ticks rise to match whatever the security budget actually is, instead of leaving a frozen subsidy assumption baked into policy. Every confirmation count anyone has ever hardcoded — “6 confs” on BTC, “X confs per deposit” anywhere — silently encodes today’s block reward. It was never noticed because the subsidy era was ambient and block time was fixed. The X/C form is the first policy where the reward is an explicit variable evaluated at spend time, so it can’t be frozen by accident. It doesn’t assume the security budget shrinks or grows — it just measures it, which means we stop ignoring block subsidy in our security assumptions. Seems obvious once you see it, but I haven’t seen it stated anywhere before.

In fact, I just had a very weird thought. As block subsidy declines and the security budget becomes fluid — hashrate and per-block rewards swinging with fee cycles, business hours vs. holidays, and so on — the question “is this transaction final?” stops having a fixed answer and becomes a continuous function of the network’s current security spend. The tick abstraction turns out to be the right primitive for this, independent of its original purpose: it gives services a uniform unit of idealized work-time against which actual accumulated reward can be measured, so finalization policy can stretch during budget lulls and contract during fee spikes automatically, instead of every service hand-rolling reward accounting on top of raw conf counts. We may have designed ticks for faster blocks and ended up with the measurement layer the fee era requires.

It also feels like this gives the network capacity. Ok, I know that sounds crazy, but hear me out. As traffic spikes, blocks get full and fee revenue goes up. This causes the network to be more secure per block, and as a consequence, value-scaled finalization thresholds are reached sooner — transactions finalize faster. Faster transactions, and programmatic CashTokens (which may be chained), all settle faster. The chain’s throughput hasn’t changed, but the velocity of every UTXO and token has — and since a UTXO can’t be respent until the receiver considers it settled, faster finalization is more settled economic activity per hour on the same block space. This is effectively giving the network more capacity, and making things go faster exactly during congestion, when the capacity is needed most. And, in a way, it’s spending the increased security budget — converting a temporary fee spike into settlement speed — rather than just letting that extra security sit unused as overhead. One caveat: the reward measurement should be over a window rather than per-block, otherwise someone could pump fees briefly to buy down their own settlement delay.

These changes could end up being so much more than just faster blocks. We might actually be building something like ABLA for transaction finalization.

2 Likes

Not eliminated — replaced by a value-scaled requirement. The number of confirmations is still whatever confirmations_ticks demands; what changes is that top-level services no longer choose a magic constant and bake in today’s security budget by accident.

The key take-away is: if you require enough confirmations_ticks to be secure against a majority attacker, you automatically require enough confirmations to be secure against a minority attacker. A majority attacker’s success probability does not decay with additional confirmations — what deters them is accumulated work, which is exactly what the tick budget measures. A minority attacker’s probability decays with each confirmation, so any count sufficient against a majority is, a fortiori, sufficient against a minority one.

Finer-grained confirmations within the same tick budget add security on top of that: at a 10-minute target a low-value deposit might need only 600 ticks, i.e. one confirmation, while at a 1-minute target the same 600 ticks is ten confirmations, each of which is another shot at exponential decay for a minority attacker.

But if majority is not your concern and you require enough confirmations_ticks against some target % minority (e.g. 33%), obviously you will not require enough against a majority, but also — if block time increases — the same tick budget spans fewer confirmations, so you will stop requiring enough against your target 33%. That is the documented trade-off between the two dimensions (Confirmation Count vs Tick Thresholds), and the reason the CHIP recommends keeping a confirmation-count floor alongside any tick threshold.

So the “calibrating” is just your policy decision about which kind of attacker you want to be safe from. “Scale confirmations proportional to value” is calibrated against a majority attacker. And since both X and C are denominated in BCH, the ratio needs no external price feed — no oracle.

This is exactly what Thorswap does, they use a formula based on your swap value: delay = delayCalc(outboundValue - (cloutScore - cloutUsed)) (source). Without ticks, they’d just divide their delay by the network’s target time to get how many confirmations they need. With ticks, they can just set their required confirmations_ticks = delay (because both are in the same units, seconds).

I’ve been researching this (Research: Block difficulty as a price oracle - #18 by bitcoincashautist). Had to put it aside while working on faster blocks, but we have all the ingredients to make the minimum fee algorithmic — and to scale the X/C security calculation not just with changing X but also with changing C, without depending on external data. But X/C are in BCH so it’s not about pinning X or C to some real-world value: 1 BCH = 1 BCH, so if external value grows or shrinks both X and C scale with it and the ratio is unchanged. Since C is expected to vary block-to-block, some smoothing would be needed, and in my oracle design I was thinking to use EWMA with a half-life to match ASERT (172,800 s, 2 days).

There’s an important silent assumption here, which the CHIP only notes briefly: from the attacker’s point of view, the X are all of his deposits he stands to claw back if he manages to reorg the target block. From a defender’s point of view it may only look like some X/K — the exchange doesn’t see where else the potential attacker is depositing at the same time, it can’t see the full X. This is still bounded by the number of venues accepting deposits at that tick threshold, so K has an upper bound (the attacker’s reachable surface) rather than being fully unknowable; you could pick a K between 1 and that bound for some safety margin.

So you’re always making an assumption about your attacker’s budget. The problem is actually 3-dimensional: attacker’s budget, attacker’s hash%, network’s coinbase value.

2 Likes

This section needs rewriting and clear specifiction: ac-0353f40e / Faster Blocks for Bitcoin Cash · GitLab

Chain-Level Floors

Chain-level minimum block size and signature-check floors are not the ABLA limit and are not scaled by the ABLA formula. Where a network defines such a floor, that floor is at least MAX_TX_SIZE and at least the per-transaction signature-check cap, so that no transaction which is valid under the transaction-level rules is unminable. The requirement is stated absolutely rather than as a clamp on a scaled value, because a value scaled below the transaction-level limits is not meaningful. A floor scaled by the ABLA formula would fall below MAX_TX_SIZE on chipnet and testnet4 at the 60-tick target time, making consensus-valid large transactions permanently unminable on those networks.

The above talks about the floor for testnets needing to be MAX_TX_SIZE, and cites that the intention is to make any consensus-valid txn mineable. MAX_TX_SIZE is a miner-only limit of 1MB per txn (and is different than the 100KB per txn for mempool and relay).

Questions:

  • Who cares? It’s a miner only limit. 100KB txns (the relay limit) can still be mined for 200KB 1-min blocks
  • If you do care, you must specify a size that has sufficient margin for coinbase txn + header – and note the coinbase txn is open-ended. Internally BCHN allocates 1000 bytes for this purpose. So minimally you need to specify something like 80 + 1000 + MAX_TX_SIZE = 1001080 bytes as the floor.
  • If that’s the floor WHY ISN’T THIS SPECIFIED EXPLICITLY IN THE CHIP? Why must one infer it from reading this AI generated text?
  • Please consider:
    • What this floor means
    • What it should be
    • If it even should exist
    • And/or how it should be applied
      • Should it be applied after ABLA on chipnet?!
      • Is ABLA ok with being told about a blocksize it is managing having been BEYOND its own internal limits?!?
        • Does the CHIP even talk about this or consider this?!
  • I want to point out that MAX_TX_SIZE has existed in Bitcoin since the BTC days and it was always a theoretical limit and you could never actually even see a txn on-chain that was MAX_TX_SIZE because that size (1MB) was the exact same limit as the block limit (1MB) and you couldn’t even mine a block that contained a header + a txn that is 1MB (with that 1MB blocksize limit in place).
6 Likes

It was an undefined spec gap identified by dagur’s AI, I left that as tripwire so we can discuss it when we get to it, and after some discussion with you: we should not add a special floor and should just let it scale down to 200kB - which can still fit max relay 100kB TXs.

Rationale: chipnet wants limited TPS, so let’s keep the same TPS, even if it means some non-standard TXs can’t fit in a block anymore. Lifting the floor to 1MB@1min would result in 5x TPS over current chipnet 2MB@10min.

Ok, so I’ll just turn that section into clarification that we’re not adding some extra floor and just let ABLA scaling do its 1/10th thing to chipnet’s 2MB base floor.

4 Likes

I’ll just turn that section into clarification that we’re not adding some extra floor and just let ABLA scaling do its 1/10th thing to chipnet’s 2MB base floor.

Sounds good to me!

4 Likes

Chain-Level Floors

No additional floor is introduced: the chain-level minimum block size scales with the target time exactly as every other ABLA output quantity does.
On networks whose base floor is 2 MB per 10-minute block (testnet3, testnet4, chipnet), the post-activation floor is 200 KB, preserving those networks’ designed transaction throughput rather than increasing it.

Consequences, accepted: post-activation usable non-coinbase space on those networks is roughly 199 KB, so at most one maximum-size standard (100 KB) transaction fits per block, and consensus-valid non-standard transactions larger than that — or, in principle, transactions at vm-limits’ maximal structural sigcheck density — cannot be mined there.
This mirrors the situation on Bitcoin prior to 2013, where a consensus-valid maximum-size transaction likewise could not fit a maximum-size block.
The per-block signature-check budget (size/141) scales with block size, so CPU load per second is preserved on every network.
Networks that need higher throughput for testing should adjust ABLA configuration rather than overriding the floor.

3 Likes

How’s this: readme.md · master · ac-0353f40e / Faster Blocks for Bitcoin Cash · GitLab

cc @mainnet_pat

based on Calin’s current wip2 implementation branch

3 Likes

The no-ticks alternative is therefore not “the same change minus the abstraction”; it is the same change with the abstraction replaced by N ad-hoc re-derivations of it, each a place to get the rounding wrong and fork.

It’s a good point. The only example I saw of it was coinbase delay. Are there are other cases?

3 Likes

Anything that is evaluated against a block span:

  • nSequence (user-set span)
  • coinbase maturity (span of 100)
  • halving (span of 210k)
  • parking depth (span of 4)
  • finalization depth (span of 10)
  • ASERT (span between anchor and tip)

With ticks, they all smoothly transition through the block time change boundary. No complicated expressions inside individual mechanisms, e.g. nSequcence enforcement is simply "an input is mature in block b if T(b) - T(c) >= 600 * n", where T(b) is the block’s tick value, a pure function of b’s height.

Tick mapper handles T(x) by checking the set tick schedule (just 1 row for our 1st switch 600->60; {H_a, 60}).

2 Likes

FWIW, This comment alone is probably as or more compelling than the current alterntative text in the CHIP. That is to say very compelling.

3 Likes

see updated section :slight_smile:

Just to provide a counterpoint – conceptually to you these are simple because you can unify them with ticks.

To someone that has to implement this – it’s like a NUKE went off in the codebase. Everything got touched.

In practice all that code got hard raped by all the changes. It’s a huge codechange either way. From my perspective as a developer I would have preferred to not rape the following things:

  • Coinbase maturity. Leave it as a height of 100. Scaling it to 1000 is confusing and surprising.
  • Block size. Don’t touch it. Marrying ticks into ABLA feels like incest. Leave it at 32mb blocks every minute. We should be so lucky if we get that much traffic.
  • nLocktime – the height mode should STAY as height. If you meant TiME, you should have used TIME mode, not HEIGHT mode. What the CHIP proposes creates a lot of developer confusion. Good luck calculating nLocktime now if you are a lowly app developer…
  • nSequence – same argument as nLocktime
2 Likes

But my point is that without ticks the same everything would’ve had to be touched – just touched in a different way (inline the arithmetics).

Your list is not an argument against ticks, but argument against touching some places at all – by preserving raw height instead of another invariant (e.g. TPS, target wall clock time).

1 Like

You didn’t read my point.

My point is you touched more than needed to be touched. See my list above where I would have been ok with not touching.

You define the list of things that needed to be touched but that’s a matter of opinion, really, not a hard requirement.

My list was smaller and didn’t need ticks.

What you did was marry lots of stuff now to PoW in a way where it wasn’t explicitly married before. Including block size! Including nLocktime/nSequence!

Is that a good idea? Debatable. Not a given. You think it’s a given, I get it.

We don’t all think that way.

2 Likes

I get your point, just saying it’s orthogonal to ticks. But yeah, you just cutting the list shorter would have reduced the surface and thus reduce relative payoff from ticks.

But should we cut the list? CHIP defends the list. e.g. 2q_x’s apps have >1k BCH TVL and they depend on NOT having height locks unlock earlier than expected.

1 Like

The payoff for ticks would be if we anticipate adjusting the blocktime further in the future. And if we want to marry as much as got married to PoW as you are marrying in this chip.

Minimally I think nLocktime and nSequence should be left alone and still mean height in height mode.

If some contract was using height as a proxy for real world time, when there already is a TIME mode, that’s on the contract.

I think it makes it incredibly hard to reason about things as an app developer to redefine nLocktime the way you have to be some “abstract height in some abstract space where blocks were still 10 mins”.

I get the motivation behind it – you can repeat it to me autistically… but my argument holds.

It’s confusing and makes nLocktime/nSequence very difficult to work with.

Of all the things in this chip – the modification of nLocktime to have a changed meaning is the one I like the least.

3 Likes