Add readExtraAliasEntryOld (tag 20) and readExtraAliasEntry (tag 33) wire readers so the node can deserialise blocks containing alias registrations. Without these readers, mainnet sync would fail on any block with an alias transaction. Three round-trip tests validate the new readers. Also apply AX-2 (comments as usage examples) across 12 files: add concrete usage-example comments to exported functions in config/, types/, wire/, chain/, difficulty/, and consensus/. Fix stale doc in consensus/doc.go that incorrectly referenced *config.ChainConfig. Co-Authored-By: Virgil <virgil@lethean.io>
128 lines
4 KiB
Go
128 lines
4 KiB
Go
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
|
//
|
|
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
|
// You may obtain a copy of the licence at:
|
|
//
|
|
// https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
|
|
//
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
// Package difficulty implements the LWMA (Linear Weighted Moving Average)
|
|
// difficulty adjustment algorithm used by the Lethean blockchain for both
|
|
// PoW and PoS blocks.
|
|
//
|
|
// The algorithm examines a window of recent block timestamps and cumulative
|
|
// difficulties to calculate the next target difficulty, ensuring blocks
|
|
// arrive at the desired interval on average. Each solve-time interval is
|
|
// weighted linearly by its recency — more recent intervals have greater
|
|
// influence on the result.
|
|
package difficulty
|
|
|
|
import (
|
|
"math/big"
|
|
)
|
|
|
|
// Algorithm constants matching the C++ source.
|
|
const (
|
|
// Window is the number of blocks in the legacy difficulty window.
|
|
Window uint64 = 720
|
|
|
|
// Lag is the additional lookback beyond the window (legacy).
|
|
Lag uint64 = 15
|
|
|
|
// Cut is the number of extreme timestamps trimmed (legacy).
|
|
Cut uint64 = 60
|
|
|
|
// BlocksCount is the total number of blocks considered (Window + Lag).
|
|
// Used by legacy algorithms; the LWMA uses LWMAWindow instead.
|
|
BlocksCount uint64 = Window + Lag
|
|
|
|
// LWMAWindow is the number of solve-time intervals used by the LWMA
|
|
// algorithm (N=60). This means we need N+1 = 61 block entries.
|
|
LWMAWindow uint64 = 60
|
|
)
|
|
|
|
// StarterDifficulty is the minimum difficulty returned when there is
|
|
// insufficient data to calculate a proper value.
|
|
var StarterDifficulty = big.NewInt(1)
|
|
|
|
// NextDifficulty calculates the next block difficulty using the LWMA algorithm.
|
|
//
|
|
// Parameters:
|
|
// - timestamps: block timestamps ordered from oldest to newest.
|
|
// - cumulativeDiffs: cumulative difficulties corresponding to each block.
|
|
// - target: the desired block interval in seconds (e.g. 120 for PoW/PoS).
|
|
//
|
|
// Returns the calculated difficulty for the next block.
|
|
//
|
|
// The algorithm matches the C++ next_difficulty_lwma() in difficulty.cpp:
|
|
//
|
|
// next_D = total_work * T * (n+1) / (2 * weighted_solvetimes * n)
|
|
//
|
|
// where each solve-time interval i is weighted by its position (1..n),
|
|
// giving more influence to recent blocks.
|
|
//
|
|
// nextDiff := difficulty.NextDifficulty(timestamps, cumulativeDiffs, 120)
|
|
func NextDifficulty(timestamps []uint64, cumulativeDiffs []*big.Int, target uint64) *big.Int {
|
|
// Need at least 2 entries to compute one solve-time interval.
|
|
if len(timestamps) < 2 || len(cumulativeDiffs) < 2 {
|
|
return new(big.Int).Set(StarterDifficulty)
|
|
}
|
|
|
|
length := len(timestamps)
|
|
|
|
// Trim to at most N+1 entries (N solve-time intervals).
|
|
maxEntries := int(LWMAWindow) + 1
|
|
if length > maxEntries {
|
|
// Keep the most recent entries.
|
|
offset := length - maxEntries
|
|
timestamps = timestamps[offset:]
|
|
cumulativeDiffs = cumulativeDiffs[offset:]
|
|
length = maxEntries
|
|
}
|
|
|
|
// n = number of solve-time intervals.
|
|
n := int64(length - 1)
|
|
T := int64(target)
|
|
|
|
// Compute linearly weighted solve-times.
|
|
// Weight i (1..n) gives more recent intervals higher influence.
|
|
var weightedSolveTimes int64
|
|
for i := int64(1); i <= n; i++ {
|
|
st := int64(timestamps[i]) - int64(timestamps[i-1])
|
|
|
|
// Clamp to [-6T, 6T] to limit timestamp manipulation impact.
|
|
if st < -(6 * T) {
|
|
st = -(6 * T)
|
|
}
|
|
if st > 6*T {
|
|
st = 6 * T
|
|
}
|
|
|
|
weightedSolveTimes += st * i
|
|
}
|
|
|
|
// Guard against zero or negative (pathological timestamps).
|
|
if weightedSolveTimes <= 0 {
|
|
weightedSolveTimes = 1
|
|
}
|
|
|
|
// Total work across the window.
|
|
totalWork := new(big.Int).Sub(cumulativeDiffs[n], cumulativeDiffs[0])
|
|
if totalWork.Sign() <= 0 {
|
|
return new(big.Int).Set(StarterDifficulty)
|
|
}
|
|
|
|
// LWMA formula: next_D = total_work * T * (n+1) / (2 * weighted_solvetimes * n)
|
|
numerator := new(big.Int).Mul(totalWork, big.NewInt(T*(n+1)))
|
|
denominator := big.NewInt(2 * weightedSolveTimes * n)
|
|
|
|
nextDiff := new(big.Int).Div(numerator, denominator)
|
|
|
|
// Ensure we never return zero difficulty.
|
|
if nextDiff.Sign() <= 0 {
|
|
return new(big.Int).Set(StarterDifficulty)
|
|
}
|
|
|
|
return nextDiff
|
|
}
|