go-blockchain/consensus/fee.go
Claude 7ac618d339
feat(consensus): fee extraction with overflow checks
TxFee calculates pre-HF4 fees as sum(inputs) - sum(outputs) with
overflow detection. Coinbase transactions return zero fee.

Co-Authored-By: Charon <charon@lethean.io>
2026-02-21 00:45:01 +00:00

79 lines
1.8 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"
"forge.lthn.ai/core/go-blockchain/types"
)
// TxFee calculates the transaction fee for pre-HF4 (v0/v1) transactions.
// Coinbase transactions return 0. For standard transactions, fee equals
// the difference between total input amounts and total output amounts.
func TxFee(tx *types.Transaction) (uint64, error) {
if isCoinbase(tx) {
return 0, nil
}
inputSum, err := sumInputs(tx)
if err != nil {
return 0, err
}
outputSum, err := sumOutputs(tx)
if err != nil {
return 0, err
}
if outputSum > inputSum {
return 0, fmt.Errorf("%w: inputs=%d, outputs=%d", ErrNegativeFee, inputSum, outputSum)
}
return inputSum - outputSum, nil
}
// isCoinbase returns true if the transaction's first input is TxInputGenesis.
func isCoinbase(tx *types.Transaction) bool {
if len(tx.Vin) == 0 {
return false
}
_, ok := tx.Vin[0].(types.TxInputGenesis)
return ok
}
// sumInputs totals all TxInputToKey amounts, checking for overflow.
func sumInputs(tx *types.Transaction) (uint64, error) {
var total uint64
for _, vin := range tx.Vin {
toKey, ok := vin.(types.TxInputToKey)
if !ok {
continue
}
if total > math.MaxUint64-toKey.Amount {
return 0, ErrInputOverflow
}
total += toKey.Amount
}
return total, nil
}
// sumOutputs totals all TxOutputBare amounts, checking for overflow.
func sumOutputs(tx *types.Transaction) (uint64, error) {
var total uint64
for _, vout := range tx.Vout {
bare, ok := vout.(types.TxOutputBare)
if !ok {
continue
}
if total > math.MaxUint64-bare.Amount {
return 0, ErrOutputOverflow
}
total += bare.Amount
}
return total, nil
}