impl storage mass as per KIP9

This commit is contained in:
Michael Sutton 2024-09-11 15:41:15 +03:00
parent 238597104f
commit fda5557f59

View File

@ -2,6 +2,7 @@ package txmass
import ( import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper" "github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
) )
@ -10,6 +11,9 @@ type Calculator struct {
massPerTxByte uint64 massPerTxByte uint64
massPerScriptPubKeyByte uint64 massPerScriptPubKeyByte uint64
massPerSigOp uint64 massPerSigOp uint64
// The parameter for scaling inverse KAS value to mass units (KIP-0009)
storageMassParameter uint64
} }
// NewCalculator creates a new instance of Calculator // NewCalculator creates a new instance of Calculator
@ -18,6 +22,7 @@ func NewCalculator(massPerTxByte, massPerScriptPubKeyByte, massPerSigOp uint64)
massPerTxByte: massPerTxByte, massPerTxByte: massPerTxByte,
massPerScriptPubKeyByte: massPerScriptPubKeyByte, massPerScriptPubKeyByte: massPerScriptPubKeyByte,
massPerSigOp: massPerSigOp, massPerSigOp: massPerSigOp,
storageMassParameter: constants.SompiPerKaspa * 10_000,
} }
} }
@ -59,6 +64,75 @@ func (c *Calculator) CalculateTransactionMass(transaction *externalapi.DomainTra
return massForSize + massForScriptPubKey + massForSigOps return massForSize + massForScriptPubKey + massForSigOps
} }
// CalculateTransactionStorageMass calculates the storage mass of the given transaction (see KIP-0009)
func (c *Calculator) CalculateTransactionStorageMass(transaction *externalapi.DomainTransaction) uint64 {
if transactionhelper.IsCoinBase(transaction) {
return 0
}
outsLen := uint64(len(transaction.Outputs))
insLen := uint64(len(transaction.Inputs))
if insLen == 0 {
panic("Storage mass calculation expects at least one input")
}
harmonicOuts := uint64(0)
for _, output := range transaction.Outputs {
inverseOut := c.storageMassParameter / output.Value
if harmonicOuts+inverseOut < harmonicOuts {
// Overflow detected. This requires 10^7 outputs so is unrealistic for wallet usages.
// If this method is ever used for consensus, this case should be handled by returning an err
panic("Unexpected overflow in storage mass calculation")
}
harmonicOuts += inverseOut
}
if outsLen == 1 || insLen == 1 || (outsLen == 2 && insLen == 2) {
harmonicDiff := harmonicOuts
for _, input := range transaction.Inputs {
if input.UTXOEntry == nil {
panic("Storage mass calculation expects a fully populated transaction")
}
inverseIn := c.storageMassParameter / input.UTXOEntry.Amount()
if harmonicDiff < inverseIn {
harmonicDiff = 0
} else {
harmonicDiff -= inverseIn
}
}
return harmonicDiff
}
sumIns := uint64(0)
for _, input := range transaction.Inputs {
if input.UTXOEntry == nil {
panic("Storage mass calculation expects a fully populated transaction")
}
// Total supply is bounded, so a sum of existing UTXO entries cannot overflow (nor can it be zero)
sumIns += input.UTXOEntry.Amount()
}
meanIns := sumIns / insLen
inverseMeanIns := c.storageMassParameter / meanIns
arithmeticIns := insLen * inverseMeanIns
if arithmeticIns < inverseMeanIns {
// overflow (so subtraction would be negative)
return 0
}
if harmonicOuts < arithmeticIns {
// underflow
return 0
} else {
return harmonicOuts - arithmeticIns
}
}
// CalculateTransactionOverallMass calculates the overall mass of the transaction including compute and storage mass components (see KIP-0009)
func (c *Calculator) CalculateTransactionOverallMass(transaction *externalapi.DomainTransaction) uint64 {
return max(c.CalculateTransactionMass(transaction), c.CalculateTransactionStorageMass(transaction))
}
// transactionEstimatedSerializedSize is the estimated size of a transaction in some // transactionEstimatedSerializedSize is the estimated size of a transaction in some
// serialization. This has to be deterministic, but not necessarily accurate, since // serialization. This has to be deterministic, but not necessarily accurate, since
// it's only used as the size component in the transaction and block mass limit // it's only used as the size component in the transaction and block mass limit