Update go.mod module line, all require/replace directives, and every .go import path from forge.lthn.ai/core/go-blockchain to dappco.re/go/core/blockchain. Add replace directives to bridge dappco.re paths to existing forge.lthn.ai registry during migration. Update CLAUDE.md, README, and docs to reflect the new module path. Co-Authored-By: Virgil <virgil@lethean.io>
80 lines
2.5 KiB
Go
80 lines
2.5 KiB
Go
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
|
//
|
|
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package consensus
|
|
|
|
import (
|
|
"fmt"
|
|
"math/bits"
|
|
|
|
coreerr "dappco.re/go/core/log"
|
|
|
|
"dappco.re/go/core/blockchain/config"
|
|
)
|
|
|
|
// BaseReward returns the base block reward at the given height.
|
|
// Height 0 (genesis) returns the premine amount. All other heights
|
|
// return the fixed block reward (1 LTHN).
|
|
func BaseReward(height uint64) uint64 {
|
|
if height == 0 {
|
|
return config.Premine
|
|
}
|
|
return config.BlockReward
|
|
}
|
|
|
|
// BlockReward applies the size penalty to a base reward. If the block
|
|
// is within the granted full reward zone, the full base reward is returned.
|
|
// If the block exceeds 2*medianSize, an error is returned.
|
|
//
|
|
// The penalty formula matches the C++ get_block_reward():
|
|
//
|
|
// reward = baseReward * (2*median - size) * size / median²
|
|
//
|
|
// Uses math/bits.Mul64 for 128-bit intermediate products to avoid overflow.
|
|
func BlockReward(baseReward, blockSize, medianSize uint64) (uint64, error) {
|
|
effectiveMedian := medianSize
|
|
if effectiveMedian < config.BlockGrantedFullRewardZone {
|
|
effectiveMedian = config.BlockGrantedFullRewardZone
|
|
}
|
|
|
|
if blockSize <= effectiveMedian {
|
|
return baseReward, nil
|
|
}
|
|
|
|
if blockSize > 2*effectiveMedian {
|
|
return 0, coreerr.E("BlockReward", fmt.Sprintf("consensus: block size %d too large for median %d", blockSize, effectiveMedian), nil)
|
|
}
|
|
|
|
// penalty = baseReward * (2*median - size) * size / median²
|
|
// Use 128-bit multiplication to avoid overflow.
|
|
twoMedian := 2 * effectiveMedian
|
|
factor := twoMedian - blockSize // (2*median - size)
|
|
|
|
// hi1, lo1 = factor * blockSize
|
|
hi1, lo1 := bits.Mul64(factor, blockSize)
|
|
|
|
// Since hi1 should be 0 for reasonable block sizes, simplify:
|
|
if hi1 > 0 {
|
|
return 0, coreerr.E("BlockReward", "consensus: reward overflow", nil)
|
|
}
|
|
hi2, lo2 := bits.Mul64(baseReward, lo1)
|
|
|
|
// Divide 128-bit result by median².
|
|
medianSq_hi, medianSq_lo := bits.Mul64(effectiveMedian, effectiveMedian)
|
|
_ = medianSq_hi // median² fits in 64 bits for any reasonable median
|
|
|
|
reward, _ := bits.Div64(hi2, lo2, medianSq_lo)
|
|
return reward, nil
|
|
}
|
|
|
|
// MinerReward calculates the total miner payout. Pre-HF4, transaction
|
|
// fees are added to the base reward. Post-HF4 (postHF4=true), fees are
|
|
// burned and the miner receives only the base reward.
|
|
func MinerReward(baseReward, totalFees uint64, postHF4 bool) uint64 {
|
|
if postHF4 {
|
|
return baseReward
|
|
}
|
|
return baseReward + totalFees
|
|
}
|