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>
79 lines
1.8 KiB
Go
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
|
|
}
|