A-TIP-RFC-MT-0004: MinoTari PoW difficulty changes
| TIP | A-TIP-RFC-MT-0004 |
|---|---|
| Title | MinoTari PoW difficulty changes |
| Last Modified | 2026-08-19 |
| Authors | SW van Heerden |
| Status | Accepted |
| Type | Architecture |
| Created | 2026-06-11 |
| References |
Overview
Tari Core currently uses a geometric-mean-style comparison to determine the best chain tip across multiple Proof-of-Work (PoW) algorithms.
The geometric mean is a measure of central tendency calculated by multiplying a set of values together and then taking the n-th root (where n is the number of values). Unlike the arithmetic mean, the geometric mean is well suited to comparing values that span multiple orders of magnitude.
Tari does not compute the full geometric mean: the final n-th root step is omitted to avoid floating-point operations. Because the protocol only needs to order two values rather than compute an exact mean, the root is unnecessary — multiplying the accumulated difficulties preserves the same ordering.
Tari currently supports four PoW algorithms:
- RxM
- RxT
- Sha3x
- C29
To compare two competing chain tips, Tari calculates:
RxM * RxT * Sha3x * C29
The chain with the larger product is considered to have more accumulated PoW.
Example
Assume the total accumulated difficulties for the four algorithms are:
RxM, RxT, Sha3x, C29
Now consider two competing new blocks:
- One mined on RxM with difficulty
x - One mined on Sha3x with difficulty
y
We compare:
(RxM + x) * RxT * Sha3x * C29
RxM * RxT * (Sha3x + y) * C29
After cancelling common terms, this comparison reduces to evaluating:
x / RxM vs y / Sha3x
Whichever ratio is larger represents the larger relative increase in accumulated PoW.
Problem
This approach works while hash rate is balanced across the algorithms. If one algorithm (for example Sha3x) sees a large hash rate increase — say because ASIC hardware becomes dominant relative to GPU mining — its relative contribution can dominate the comparison.
As accumulated difficulty grows, terms such as x / RxM converge toward
zero unless x scales proportionally with RxM. If one algorithm’s
difficulty grows much faster than the others, its blocks consistently
produce larger relative increases.
One algorithm can therefore reorg blocks mined by the other algorithms disproportionately often, which undermines the original design goal of using multiple PoW algorithms for decentralization and hardware diversity.
Proposed Change 1
Tari currently calculates target difficulty using an LWMA (Linearly Weighted Moving Average) over the last 90 blocks. The LWMA uses:
- Target block time
- Header timestamps
- Historical target difficulties
to compute the next target difficulty.
Threat model
The mechanism proposed here targets a miner whose hash power is concentrated in a single PoW algorithm and who therefore has to mine consecutive blocks with that one algorithm in order to build a competing chain. The canonical case is Sha3x ASIC capacity growing until it can routinely out-pace the GPU and CPU algorithms.
It does not defend against a miner who controls capacity in two or more algorithms and can alternate between them. Such a miner never produces a same-algorithm run and never incurs a penalty. This limitation is inherent to a rule keyed on the previous block’s algorithm.
Because RxM and RxT are both RandomX variants mined by the same hardware, that exemption covers RandomX capacity by construction: a miner with CPU hash rate can alternate the two variants at no cost. This is a deliberate choice, discussed under Penalty scope below, and it means the mechanism binds Sha3x and C29 in practice.
Penalty scope
The backoff is keyed on the PoW algorithm of the preceding blocks. All four algorithms — RxM, RxT, Sha3x and C29 — are treated independently. A run is a sequence of consecutive blocks of the same algorithm, and a block of any other algorithm resets the modifier.
An earlier draft of this RFC grouped RxM and RxT into a single RandomX penalty class, on the grounds that both are mined by the same hardware and a RandomX farm could otherwise alternate them to bypass the mechanism. That grouping is not adopted, for two reasons.
First, it is not neutral between algorithms. All four algorithms are configured with the same base target time, and each algorithm’s LWMA pins that algorithm to its own target time independently of hash rate, so in steady state each wins roughly one quarter of blocks. A combined RandomX class would therefore hold roughly half of all blocks and pay a far larger expected penalty than Sha3x — about 1.47x against about 1.15x in simulation. The mechanism would tax the CPU-mined algorithms hardest while the Sha3x concentration it was written to deter paid least.
Second, the grouping is the dominant contributor to the block time inflation described below. It raises the mean block interval by roughly 8%, against roughly 2% with the algorithms kept separate.
The cost of keeping them separate is that a miner with RandomX capacity can alternate RxM and RxT and pay no penalty at all, including when building a private chain. This is accepted: it is the same exemption already granted to any miner holding capacity in two algorithms, and the threat model above names single-algorithm Sha3x concentration as the target.
Penalty scope affects only the backoff rule. Accumulated difficulty is still tracked per algorithm, and the chain comparison in the Overview is unchanged.
The backoff rule
This RFC introduces an exponential backoff for consecutive blocks mined by the same algorithm:
- If a block is mined with algorithm A and the previous block was also mined with algorithm A, the target time for A is doubled.
- The doubling compounds for each further consecutive block of the same algorithm.
- If a block of any other algorithm is mined, the target time for A resets to its base consensus value.
Cap
The doubling is capped at 32x. Writing r for the run length — the
number of consecutive blocks of the same algorithm ending at and
including the block being mined — the modifier is:
m = min( 2^(r - 1), 32 )
Run length r | Modifier m |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 4 |
| 4 | 8 |
| 5 | 16 |
| 6 | 32 |
| 7 or more | 32 |
The cap is a liveness requirement, not a tuning choice. The modifier only resets when a block of a different algorithm is mined. If an algorithm is ever the sole active miner — for example if the other algorithms lose their miners after a fork — there is nothing to reset it, and an uncapped modifier would double indefinitely until the chain stopped producing blocks, with no recovery path short of another fork. The cap bounds that failure to a 32x slowdown: at an 8 minute base target, a chain mined by a single algorithm settles at roughly 4.3 hours per block and resumes normal operation as soon as any other algorithm mines. A chain left with only RandomX miners does not reach this state, because RxM and RxT reset each other.
The cap is reached after 5 doublings, i.e. on the sixth consecutive block of an algorithm.
Expected cost, and why the factor is two
The doubling factor is not arbitrary. It places the point at which the mechanism becomes prohibitive exactly at the majority threshold.
Model each block’s algorithm as independent, with p the share of
blocks won by algorithm A. For a block mined by A, let j be the number
of immediately preceding consecutive A blocks, so that m = 2^j. The
j preceding blocks are A and the one before them is not, giving:
P(j = k) = p^k * (1 - p)
The expected modifier paid by a block of algorithm A is therefore:
E[m] = sum over k of (1 - p) * p^k * 2^k
= (1 - p) / (1 - 2p) for p < 1/2
The series converges only while 2p < 1. Below 50% block share the
expected penalty is finite and mild; as share approaches 50% it grows
without bound. More generally, a backoff factor of b places the
divergence at p = 1/b: a factor of 1.5 would only become prohibitive
above a two-thirds share, and a factor of 3 would penalize an algorithm
holding only a third of blocks. A factor of two is the choice that
puts the pole at the majority threshold, which is the threshold the
protocol already treats as the security boundary.
With the 32x cap the expectation is finite everywhere, since a run of five or more preceding blocks pays a flat 32:
E[m] = (1 - p) * (1 - (2p)^5) / (1 - 2p) + 32 * p^5
Block share p | E[m] uncapped | E[m] capped at 32 |
|---|---|---|
| 0.25 (four algos) | 1.50 | 1.48 |
| 1/3 (three algos) | 2.00 | 1.87 |
| 0.40 | 3.00 | 2.34 |
| 0.45 | 5.50 | 2.84 |
| 0.50 | diverges | 3.50 |
| 0.60 | diverges | 5.47 |
| 0.75 | diverges | 10.89 |
The cap is a deliberate trade. It buys the liveness guarantee described above, and it pays for that by blunting the sharp majority threshold: an algorithm at 50% share pays 3.5x rather than an unbounded amount. The mechanism therefore taxes concentration progressively rather than imposing a hard ceiling on it.
Why these figures are an upper bound
The independent-arrival model above is what fixes the backoff factor at
two, but it overstates the penalty actually paid, and by a wide margin.
It ignores the feedback the mechanism itself creates: while an algorithm
holds the tip it is throttled by m and the other three are not, so it
is markedly less likely to extend its own run than p suggests.
Simulating the race between algorithms through to the LWMA fixed point, at four live algorithms with equal base target times:
Run length r | P(extend) modelled | P(extend) simulated |
|---|---|---|
| 1 | 0.25 | 0.13 |
| 2 | 0.25 | 0.07 |
| 3 | 0.25 | 0.04 |
| 4 | 0.25 | 0.03 |
Long runs are therefore far rarer than p^k, and long runs are exactly
what the 2^k weight multiplies. The realised expected modifier is
correspondingly lower:
| Live algorithms | E[m] modelled | E[m] simulated |
|---|---|---|
| 4 | 1.48 | 1.16 |
| 3 | 1.87 | 1.23 |
Note also that p is a share of blocks won, not of hash rate, and the
two are only loosely coupled here. Because each algorithm’s LWMA pins it
to its own target time, steady-state block share is set by the
configured target times and is insensitive to hash rate: a Sha3x hash
rate boom raises Sha3x difficulty rather than its block share. Shares
materially above 1/4 arise from algorithms losing their miners
altogether, not from one algorithm out-hashing the others. The rows at
p >= 0.4 above are therefore about attack and abandonment scenarios,
not about ordinary operation.
Effect on mean block time
Because solve times are normalized by m before entering the LWMA, the
LWMA has no feedback path that fully corrects for the penalty. It
converges to the point where the normalized mean solve time equals the
base target time T, while wall-clock intervals remain longer than
that. Block production is therefore slower than the configured target.
A first-order estimate treats the modifier and the solve time as
independent, giving E[solve_time] = E[m] * T. That overstates the
effect substantially, for three reasons:
- Runs are suppressed by the penalty itself, so
E[m]is much smaller than the independent model predicts. See Why these figures are an upper bound above. - The chain does not stall while an algorithm is penalized. Only the
algorithm holding the tip is throttled; the other three continue at
full rate, so total block production dips rather than scaling down
by
m. - The normalization does not fully blind the LWMA. Tari keeps one LWMA per algorithm, so a solve time is the gap between consecutive blocks of that algorithm and spans blocks of other algorithms. Penalties in force during the gap stretch it, but only the modifier of the block that closes the gap is divided out. The residual stretch is visible to the LWMA, which lowers difficulty in response and partially compensates.
Simulating the race through to the LWMA fixed point at the mainnet configuration — four algorithms with equal base target times — gives the following inflation of the mean chain block interval:
| Configuration | Inflation |
|---|---|
| Four algorithms live | 1.7% |
| Three algorithms live (one abandoned) | 3.5% |
| Single algorithm live | 32x (the cap) |
At under 2% the effect does not warrant reducing the base target times to compensate, and doing so would misconfigure the chain if the number of live algorithms later changed. For comparison, grouping RxM and RxT into one penalty class raises the four-algorithm figure to 8.4% and the three-algorithm figure to 19.6%, which is the second reason that grouping is not adopted.
The inflation that does remain is a consequence of normalizing solve times rather than letting the LWMA absorb the penalty. The alternative — omitting the normalization — holds block time exactly constant but lets the LWMA cancel the penalty in steady state, leaving it effective only transiently.
Difficulty calculation
The current difficulty calculation is:
next_target_difficulty = ave_difficulty * k / weighted_times
where k = block_window * (block_window + 1) * target_time / 2
Because the penalty enters the formula only through target_time, it
can be expressed as a single multiplier:
adjusted_next_target_difficulty = next_target_difficulty * m
where m = the penalty modifier
The adjusted_next_target_difficulty is used only as the target
difficulty that the mined block must meet. It is not counted when
calculating the total accumulated PoW of the chain — the unadjusted
difficulty is used there.
The modifier applied to each block in the window must also be fed back into the next difficulty calculation. The weighted times are currently the sum over the window of:
weighted_times = sum( solve_time[i] * (i + 1) )
where i = index of the block within the window
Each solve time is normalized by the modifier that was in force for that block, so that a block mined against an inflated target is not read as a drop in hash rate:
weighted_times = sum( solve_time[i] / m[i] * (i + 1) )
where
i = index of the block within the window
m[i] = modifier applied to block i
Order of normalization and clamping
The existing LWMA clamps each solve time before use, to bound the influence of manipulated timestamps. The normalization above interacts with that clamp, and the order is consensus-critical.
Normalization is applied first, and the clamp is then applied to the normalized value against the unchanged base bounds:
solve_time[i] = clamp( raw_solve_time[i] / m[i], min_bound, max_bound )
Clamping the raw solve time against the base bounds and dividing
afterwards is incorrect. A block legitimately mined against a 32x target
takes roughly 32x longer, so the base upper bound would truncate a real
solve time and the subsequent division would yield a spuriously small
value, driving a false difficulty spike. Because m[i] is always
positive, clamping the normalized value against the base bounds is
exactly equivalent to clamping the raw value against bounds scaled by
m[i], which is the intended behaviour.
Integer arithmetic
solve_time[i] / m[i] must not be evaluated as an integer division per
term: with m = 32 a 15 second solve time truncates to zero, and the
error accumulates across every block in the window. Because every
modifier is a power of two dividing the 32x cap, the sum can be kept in
exact integer arithmetic by scaling every term by the cap:
weighted_times = sum( raw_solve_time[i] * (i + 1) * (M_MAX / m[i]) )
k = block_window * (block_window + 1) * target_time * M_MAX / 2
where M_MAX = 32
M_MAX / m[i] is an exact integer for every permitted modifier, and
scaling both k and weighted_times by the same factor leaves
next_target_difficulty unchanged.
This mechanism:
- Does not alter the underlying hash rate.
- Does not directly manipulate accumulated difficulty.
- Makes selfish mining exponentially more expensive for any single algorithm attempting to dominate.
- Encourages natural interleaving of algorithms.
Example
Assume the base target time for Sha3x is 8 minutes.
- If the previous block was Sha3x, the next Sha3x target time becomes 16 minutes.
- If another Sha3x block is mined consecutively, the target time becomes 32 minutes.
- After six consecutive Sha3x blocks the target time reaches 4 hours 16 minutes (32x) and stops growing.
- If a block of any other algorithm is mined, the Sha3x target time resets to 8 minutes.
An RxM block followed by an RxT block is a run of one in each algorithm, so neither block is penalized. Switching between the two RandomX variants resets the modifier, and a miner with RandomX capacity can alternate them indefinitely without ever paying a penalty.
Consequences 1
Positive
- Significantly increases the cost of selfish mining. A private chain of depth 6 built by a single algorithm costs 63 units of work against 6 for an honest chain of the same depth.
- Reduces the ability of a single algorithm to reorg multiple blocks.
- Preserves the multi-algorithm decentralization objective.
- Treating every algorithm independently keeps the mechanism neutral between them: all four carry the same steady-state block share and the same expected modifier, so no algorithm is taxed more than another for mining honestly.
- The cost to honest mining is small — about 1.16x on average with four algorithms live — while remaining progressive in concentration.
Negative
- Requires a hard fork.
- Changes block-time dynamics under certain hash rate distributions.
- May introduce more short-term variance in block intervals.
- Inflates the mean block interval by roughly 2% with four algorithms live, rising as algorithms are abandoned. See Effect on mean block time.
- The 32x cap blunts the majority threshold: an algorithm at 50% block share pays 3.5x on average rather than an unbounded amount.
- Does not prevent a miner holding capacity in two or more algorithms from alternating between them to avoid the penalty entirely. Because RxM and RxT run on the same hardware, this exempts RandomX capacity in full, including when building a private chain.
- If a single algorithm is left as the only active miner, the chain runs at up to 32x the base target time until another algorithm mines a block.
- Penalizes a single-algorithm miner for a run that occurs by chance, not only for one produced deliberately.
Neutral
- Does not change the geometric-mean comparison logic directly.
- Modifies the LWMA only through its target-time input and the corresponding normalization of solve times; the shape of the moving average is unchanged.
- Requires no new header fields. The modifier for any block is derived from the PoW algorithms of the preceding blocks, so validators can recompute it from headers alone — noting that this requires up to 5 headers of lookback beyond the start of the LWMA window.
Proposed Change 2
Tari currently uses a block window of 90 blocks for the LWMA. This gives a stable difficulty, but it takes several blocks to respond to a change in hash rate. When large miners switch on and off, the resulting hash rate swings produce correspondingly large swings in solve time, so a faster response is desirable. Given Tari’s long target block time, the window should be reduced to 45 blocks.
Consequences 2
Positive
- Responds faster to an increase or decrease in hash power.
Negative
- Can cause more oscillation in the difficulty.
References
- https://github.com/zawy12/difficulty-algorithms/issues/3#issuecomment-442129791
- https://github.com/zcash/zcash/issues/4021
Change History
2026-06-11
- Document Created.
2026-08-19
- RxM and RxT are no longer grouped into a shared RandomX penalty class; every algorithm is its own penalty scope.
- Corrected the Effect on mean block time analysis. The previous figure of 87% came from the independent-arrival model, which ignores that the penalty suppresses the runs it taxes, that unpenalized algorithms keep mining at full rate, and that the per-algorithm LWMA is only partially blinded by the normalization. Simulated inflation is 1.7% with four algorithms live, and the base target times no longer need to be reduced to compensate.