kaspad/util/amount.go
stasatdaglabs 3dbc42b4f7
Implement the new block subsidy function (#1830)
* Replace the old blockSubsidy parameters with the new ones.

* Return subsidyGenesisReward if blockHash is the genesis hash.

* Traverse a block's past for the subsidy calculation.

* Partially implement SubsidyStore.

* Refer to SubsidyStore from CoinbaseManager.

* Wrap calcBlockSubsidy in getBlockSubsidy, which first checks the database.

* Fix finalityStore not calling GenerateShardingID.

* Implement calculateAveragePastSubsidy.

* Implement calculateMergeSetSubsidySum.

* Implement calculateSubsidyRandomVariable.

* Implement calcBlockSubsidy.

* Add a TODO about floats.

* Update the calcBlockSubsidy TODO.

* Use binary.LittleEndian in calculateSubsidyRandomVariable.

* Fix bad range in calculateSubsidyRandomVariable.

* Replace float64 with big.Rat everywhere except for subsidyRandomVariable.

* Fix a nil dereference.

* Use a random walk to approximate the normal distribution.

* In order to avoid unsupported fractional results from powInt64, flip the numerator and the denominator manually.

* Set standardDeviation to 0.25, MaxSompi to 10_000_000_000 * SompiPerKaspa and defaultSubsidyGenesisReward to 1_000.

* Set the standard deviation to 0.2.

* Use a binomial distribution instead of trying to estimate the normal distribution.

* Change some values around.

* Clamp the block subsidy.

* Remove the fake duplicate constants in the util package.

* Reduce MaxSompi to only 100m Kaspa to avoid hitting the uint64 ceiling.

* Lower MaxSompi further to avoid new and exciting ways for the uint64 ceiling to be hit.

* Remove debug logs.

* Fix a couple of failing tests.

* Fix TestBlockWindow.

* Fix limitTransactionCount sometimes crashing on index-out-of-bounds.

* In TrustedDataDataDAABlock, replace BlockHeader with DomainBlock

* In calculateAveragePastSubsidy, use blockWindow instead of doing a BFS manually.

* Remove the reference to DAGTopologyManager in coinbaseManager.

* Add subsidy to the coinbase payload.

* Get rid of the subsidy store and extract subsidies out of coinbase transactions.

* Keep a blockWindow amount of blocks under the virtual for IBD purposes.

* Manually remove the virtual genesis from the merge set.

* Fix simnet genesis.

* Fix TestPruning.

* Fix TestCheckBlockIsNotPruned.

* Fix TestBlockWindow.

* Fix TestCalculateSignatureHashSchnorr.

* Fix TestCalculateSignatureHashECDSA.

* Fix serializing the wrong value into the coinbase payload.

* Rename coinbaseOutputForBlueBlock to coinbaseOutputAndSubsidyForBlueBlock.

* Add a TODO about optimizing trusted data DAA window blocks.

* Expand on a comment in TestCheckBlockIsNotPruned.

* In calcBlockSubsidy, divide the big.Int numerator by the big.Int denominator instead of converting to float64.

* Clarify a comment.

* Rename SubsidyMinGenesisReward to MinSubsidy.

* Properly handle trusted data blocks in calculateMergeSetSubsidySum.

* Use the first two bytes of the selected parent's hash for randomness instead of math/rand.

* Restore maxSompi to what it used to be.

* Fix TestPruning.

* Fix TestAmountCreation.

* Fix TestBlockWindow.

* Fix TestAmountUnitConversions.

* Increase the timeout in many-tips to 30 minutes.

* Check coinbase subsidy for every block

* Re-rename functions

* Use shift instead of powInt64 to determine subsidyRandom

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-10-30 10:16:47 +03:00

123 lines
4.0 KiB
Go

// Copyright (c) 2013, 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package util
import (
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/pkg/errors"
"math"
"strconv"
)
// AmountUnit describes a method of converting an Amount to something
// other than the base unit of a kaspa. The value of the AmountUnit
// is the exponent component of the decadic multiple to convert from
// an amount in kaspa to an amount counted in units.
type AmountUnit int
// These constants define various units used when describing a kaspa
// monetary amount.
const (
AmountMegaKAS AmountUnit = 6
AmountKiloKAS AmountUnit = 3
AmountKAS AmountUnit = 0
AmountMilliKAS AmountUnit = -3
AmountMicroKAS AmountUnit = -6
AmountSompi AmountUnit = -8
)
// String returns the unit as a string. For recognized units, the SI
// prefix is used, or "Sompi" for the base unit. For all unrecognized
// units, "1eN KAS" is returned, where N is the AmountUnit.
func (u AmountUnit) String() string {
switch u {
case AmountMegaKAS:
return "MKAS"
case AmountKiloKAS:
return "kKAS"
case AmountKAS:
return "KAS"
case AmountMilliKAS:
return "mKAS"
case AmountMicroKAS:
return "μKAS"
case AmountSompi:
return "Sompi"
default:
return "1e" + strconv.FormatInt(int64(u), 10) + " KAS"
}
}
// Amount represents the base kaspa monetary unit (colloquially referred
// to as a `Sompi'). A single Amount is equal to 1e-8 of a kaspa.
type Amount uint64
// round converts a floating point number, which may or may not be representable
// as an integer, to the Amount integer type by rounding to the nearest integer.
// This is performed by adding or subtracting 0.5 depending on the sign, and
// relying on integer truncation to round the value to the nearest Amount.
func round(f float64) Amount {
if f < 0 {
return Amount(f - 0.5)
}
return Amount(f + 0.5)
}
// NewAmount creates an Amount from a floating point value representing
// some value in kaspa. NewAmount errors if f is NaN or +-Infinity, but
// does not check that the amount is within the total amount of kaspa
// producible as f may not refer to an amount at a single moment in time.
//
// NewAmount is for specifically for converting KAS to Sompi.
// For creating a new Amount with an int64 value which denotes a quantity of Sompi,
// do a simple type conversion from type int64 to Amount.
func NewAmount(f float64) (Amount, error) {
// The amount is only considered invalid if it cannot be represented
// as an integer type. This may happen if f is NaN or +-Infinity.
switch {
case math.IsNaN(f):
fallthrough
case math.IsInf(f, 1):
fallthrough
case math.IsInf(f, -1):
return 0, errors.New("invalid kaspa amount")
}
return round(f * constants.SompiPerKaspa), nil
}
// ToUnit converts a monetary amount counted in kaspa base units to a
// floating point value representing an amount of kaspa.
func (a Amount) ToUnit(u AmountUnit) float64 {
return float64(a) / math.Pow10(int(u+8))
}
// ToKAS is the equivalent of calling ToUnit with AmountKAS.
func (a Amount) ToKAS() float64 {
return a.ToUnit(AmountKAS)
}
// Format formats a monetary amount counted in kaspa base units as a
// string for a given unit. The conversion will succeed for any unit,
// however, known units will be formated with an appended label describing
// the units with SI notation, or "Sompi" for the base unit.
func (a Amount) Format(u AmountUnit) string {
units := " " + u.String()
return strconv.FormatFloat(a.ToUnit(u), 'f', -int(u+8), 64) + units
}
// String is the equivalent of calling Format with AmountKAS.
func (a Amount) String() string {
return a.Format(AmountKAS)
}
// MulF64 multiplies an Amount by a floating point value. While this is not
// an operation that must typically be done by a full node or wallet, it is
// useful for services that build on top of kaspa (for example, calculating
// a fee by multiplying by a percentage).
func (a Amount) MulF64(f float64) Amount {
return round(float64(a) * f)
}