CHIP-2025-03 Faster Blocks for Bitcoin Cash

Sure, although I wonder if implementing that would be as simple. I imagine swapping confirmations with confirmation_ticks is a drop-in config change for many multi-coin services (just update field name and threshold).
Supporting multiple criterion fields would require some more integration work, I imagine.

I intend to add a little integration guide, that would answer: “Consumers of what data will need to make what changes to achieve status quo behavior in their systems?” so we can cover it there. Maybe a section, maybe a stand-alone document to go with BCHN release notes.

2 Likes

Is there a derived security metric we could have, giving an abstracted security threshold so people don’t need to think about the details? Lets say we exposed a security_score that was a logarithmically weighted score that varied from 1 to 10.

For example, 1 could be 90% confident, 2 could be 99% confident, 3 could be 99.9% confident, etc. Or alternatively, for 10% attack success you’d need x amount of hash for 1, or 10x amount of hash for 2, or 100x amount of hash for 3, etc.

Then, users choose a threshold level that they need.

The thresholds could be based on a function that took into account ticks, wall time, and number of confirmations, and other security features of how the chain works. Depending on block time, number of confirmations might dominate or ticks might dominate. But setting to a 3 is always the same security level.

(this is something that might be worth sleeping on and thinking about)

1 Like

Good question, although I dislike burdening node API with this “security level” abstraction. Rather than that, I was thinking of just some user-friendly security guidance note, because I found that “minority’s odds go down with confirmation count no matter the block time” is a forgotten ancient knowledge despite it having a whole section in the WP :slightly_smiling_face:

While working out the answer I found that confirmations_ticks is probably enough, because even 1-conf is uneconomic for a minority hash attacker – unless the pot is big enough. But if the pot is tempting enough for a minority attacker, then it is even more tempting for a majority attacker, so you will want higher confirmations_ticks anyway and with it you will get confirmations against the minority automatically, so linear scaling the required confirmations_ticks based on deposited value is always safe against a minority attacker, it’s actually more safety than needed against minority, not less. No wonder it became a thumb rule and everyone forgot the full why.

TL;DR

Scale required confirmations_ticks with deposit value and you’re protected from both majority and minority, at any block time — provided you calibrate to the majority requirement. The minority curve always sits below the majority line, so the value-scaled ticks rule over-covers the minority for free. That’s the whole trick: it was calibrated to the worst case all along, and the minority protection was a side effect, not the reason.

We can’t practically have a unified “security” slider for the minority case, because the minority case is 2-dimensional: the recipient has to specify what %hash they want to be safe from, and only then can they read off the number of confs for the given amount.

See analysis below.


Note that parking & finalization already made 1-conf much safer:

These are the odds of reversing a 1-conf if you keep racing until the end (bounded by finalization, infinity with plain Satoshi). “Fair” means some % of hashpower gives you the same % chance of success. But that’s just “did you or did you not manage to reorg that 1 block you were after”, it says nothing about costs vs payout. And if you’re racing until the end, the payout is not fair: you’re risking losing 10 coinbases just to win a fixed reward X in that 1 block you’re after. So it’s really not economic to continue racing after you fall behind, unless you have >50% hash.

Below models the “give up if fall behind by 1 block” profitability (extracted from CHIP’s security.md):

So, suppose attacker deposits 100 BCH. Without finalization, the exchange should ask for 100/3.125 10-min confs (32 confs!) if they fear a supermajority miner, but only 5 confs if they fear a <33% hash attacker. BTC has no finalization, yet it accepts 100 BTC with much less than a 32-confirmation requirement — how so? Because it believes >50% is honest, the assumption on which Bitcoin’s design relied from the start.

What if the attacker deposits 10 BCH? The exchange should ask for 10/3.125 10-min confs (4 confs) to protect against a supermajority, but only 1 conf against the <33% attacker.

What if he deposits 1 BCH? 1-conf, and it’s protected against both minority and supermajority.

Deposit vs ≤33% attacker vs >75% attacker
1 BCH 1 conf 1 conf
10 BCH 1 conf 4 confs
100 BCH 5 confs 32 confs

With 1-minute blocks that changes, but notice it takes less than 10x confirmations vs the minority and exactly 10x vs the supermajority.

Deposit vs ≤33% attacker vs >75% attacker
1 BCH 1 conf 4 confs
10 BCH 5 confs 32 confs
100 BCH 14 confs 320 confs

Here’s the catch: if you based your policy against a 33% attacker and stored it in ticks, slower blocks would expose you. Take the 100 BCH deposit: 14 confs × 60 = 840 ticks at 1-minute. If the chain later slowed back to 10-minute blocks, 840 ticks is barely 1–2 confs — but against a 33% attacker, 100 BCH needs 5 confs there. The exposure is real, because in the minority domain things are non-linear: finer granularity offers more security at the same tick threshold, and coarser granularity offers less.

So the thumb rule holds at any block time only when you calibrate to the supermajority requirement (ticks ∝ value). That line is a pure economic ratio — X/C — and it’s block-time-invariant. The minority requirement is not invariant in ticks, but it’s also always cheaper than the supermajority one, so as long as you’re over-insuring on the majority line you never have to think about the minority at all. The moment you start trusting honest majority and basing the number on the minority curve, you’ve made a bet about q — and then you must express it in confirmations, not ticks, or a future block-time change can quietly under-cut it.

3 Likes

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