Difficulty Algorithm
AgentChain uses a custom difficulty adjustment algorithm called CalcDifficultyAgentChain. It is designed to converge on a 6-second block time target while remaining simple, predictable, and free of any difficulty bomb mechanism.
Overview
The difficulty algorithm adjusts the mining difficulty of each new block based on how long it took to mine the parent block. If blocks are coming too fast, difficulty increases. If blocks are coming too slow, difficulty decreases. If the block time is within an acceptable range, difficulty stays the same.
Key properties:
- Target block time: 6 seconds
- No difficulty bomb: Unlike Ethereum mainnet, there is no exponential difficulty increase over time.
- Minimum difficulty floor: 131,072 (0x20000), matching the genesis difficulty.
- Smooth adjustment: Changes are made in increments of
parent_difficulty / 2048, preventing sudden jumps.
Pseudocode
function CalcDifficultyAgentChain(time, parent):
// Target block time: 6 seconds
target = 6
// Time difference from parent
diff = time - parent.time
// Adjustment factor
if diff < target:
adjustment = 1 // increase difficulty
else if diff >= target and diff < target * 2:
adjustment = 0 // no change
else:
adjustment = -1 // decrease difficulty
// Apply adjustment
// adjustment_step = parent.difficulty / 2048
new_difficulty = parent.difficulty + (parent.difficulty / 2048) * adjustment
// Minimum difficulty
if new_difficulty < 131072:
new_difficulty = 131072
return new_difficulty
How It Works
Three Zones of Adjustment
The algorithm divides block times into three zones:
| Block Time | Condition | Adjustment | Effect |
|------------|-----------|------------|--------|
| < 6 seconds | diff < target | +1 | Difficulty increases — blocks are too fast |
| 6-11 seconds | diff >= target && diff < target * 2 | 0 | No change — block time is acceptable |
| >= 12 seconds | diff >= target * 2 | -1 | Difficulty decreases — blocks are too slow |
The "acceptable" zone spans from exactly the target (6 seconds) up to but not including double the target (12 seconds). This dead zone prevents oscillation by allowing minor variations in block time without triggering adjustments.
Adjustment Step Size
Each adjustment step is exactly parent.difficulty / 2048. This means:
- At difficulty 131,072 (minimum), each step is 64.
- At difficulty 1,000,000, each step is approximately 488.
- At difficulty 1,000,000,000, each step is approximately 488,281.
The step size scales proportionally with difficulty, ensuring that adjustments are always a consistent fraction (~0.049%) of the current difficulty regardless of the absolute value.
Minimum Difficulty Floor
The difficulty can never fall below 131,072 (0x20000). This floor prevents two problems:
- Trivial mining: Without a floor, difficulty could theoretically reach 1, allowing instant block creation.
- Division by zero protection: Since the adjustment step is
difficulty / 2048, extremely low difficulties could produce a step size of 0, causing the algorithm to stall.
The floor value of 131,072 was chosen because it produces a minimum adjustment step of 64, which is large enough for meaningful adjustments while being low enough for initial bootstrapping.
Comparison with Ethereum Mainnet
| Property | Ethereum (pre-Merge) | AgentChain | |----------|---------------------|------------| | Target block time | ~13 seconds | 6 seconds | | Difficulty bomb | Yes (ice age) | No | | Uncle consideration | Yes (adjusts for uncles) | No | | Adjustment zones | Continuous formula | Three discrete zones | | Step size | parent.difficulty / 2048 | parent.difficulty / 2048 | | Minimum difficulty | 131,072 | 131,072 |
The key differences are:
- No difficulty bomb. Ethereum's difficulty bomb was a mechanism to force hard forks by making mining exponentially harder over time. AgentChain has no such mechanism — difficulty is purely a function of block times.
- No uncle adjustment. Ethereum increased difficulty when uncle blocks were present (indicating network congestion). AgentChain ignores uncles entirely in the difficulty calculation.
- Simpler adjustment logic. Ethereum used a more complex formula with a continuous adjustment factor. AgentChain uses three discrete zones for clarity and predictability.
Convergence Behavior
The algorithm converges on the 6-second target through a self-correcting feedback loop:
Scenario: Hashrate Increase
- A large miner joins the network.
- Blocks start arriving faster than 6 seconds (e.g., 3 seconds).
- The algorithm detects
diff < targetand setsadjustment = +1. - Difficulty increases by
difficulty / 2048on each block. - This continues until block times return to the 6-12 second range.
- Once in range,
adjustment = 0and difficulty stabilizes.
Scenario: Hashrate Decrease
- A large miner leaves the network.
- Blocks start arriving slower than 12 seconds (e.g., 20 seconds).
- The algorithm detects
diff >= target * 2and setsadjustment = -1. - Difficulty decreases by
difficulty / 2048on each block. - This continues until block times return to the 6-12 second range.
Convergence Speed
Because each adjustment is approximately 0.049% of the current difficulty, recovering from a large hashrate change requires many blocks. For example, if hashrate doubles overnight:
- Block times halve (from ~6s to ~3s).
- Each block increases difficulty by ~0.049%.
- It takes approximately 1,400 blocks (~2.3 hours at 6s/block) for difficulty to double and restore equilibrium.
This gradual adjustment prevents abrupt difficulty spikes that could destabilize the network.
Implementation Notes
Go Implementation Reference
In the Go-ethereum (Geth) codebase, the difficulty calculation is implemented in the consensus engine. The AgentChain variant replaces the standard calcDifficultyFrontier / calcDifficultyHomestead / makeDifficultyCalculator functions with the custom CalcDifficultyAgentChain function.
func CalcDifficultyAgentChain(time uint64, parent *types.Header) *big.Int {
target := uint64(6)
diff := time - parent.Time
var adjustment int64
if diff < target {
adjustment = 1
} else if diff < target*2 {
adjustment = 0
} else {
adjustment = -1
}
// adjustment_step = parent.Difficulty / 2048
adjustmentStep := new(big.Int).Div(parent.Difficulty, big.NewInt(2048))
// new_difficulty = parent.Difficulty + adjustmentStep * adjustment
result := new(big.Int).Set(parent.Difficulty)
result.Add(result, new(big.Int).Mul(adjustmentStep, big.NewInt(adjustment)))
// Enforce minimum difficulty
minDifficulty := big.NewInt(131072)
if result.Cmp(minDifficulty) < 0 {
result.Set(minDifficulty)
}
return result
}Edge Cases
- Genesis block: The genesis block has difficulty 131,072. The first mined block (block 1) uses this as the parent difficulty.
- Timestamp manipulation: Miners could theoretically set a future timestamp to trigger a difficulty decrease. However, Geth rejects blocks with timestamps too far in the future (typically 15 seconds ahead of local time), limiting this attack vector.
- Zero time delta: If
time == parent.time(same second),diff = 0, which is less than the target. Difficulty increases, discouraging timestamp collision.
Monitoring Difficulty
You can monitor the current network difficulty through the JSON-RPC API:
curl -X POST http://localhost:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}'The difficulty field in the response shows the current block's difficulty in hexadecimal.