kaspad/util/amount_test.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

263 lines
5.2 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_test
import (
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"math"
"testing"
. "github.com/kaspanet/kaspad/util"
)
func TestAmountCreation(t *testing.T) {
tests := []struct {
name string
amount float64
valid bool
expected Amount
}{
// Positive tests.
{
name: "zero",
amount: 0,
valid: true,
expected: 0,
},
{
name: "max producible",
amount: 21e6,
valid: true,
expected: constants.MaxSompi,
},
{
name: "exceeds max producible",
amount: 21e6 + 1e-8,
valid: true,
expected: constants.MaxSompi + 1,
},
{
name: "one hundred",
amount: 100,
valid: true,
expected: 100 * constants.SompiPerKaspa,
},
{
name: "fraction",
amount: 0.01234567,
valid: true,
expected: 1234567,
},
{
name: "rounding up",
amount: 54.999999999999943157,
valid: true,
expected: 55 * constants.SompiPerKaspa,
},
{
name: "rounding down",
amount: 55.000000000000056843,
valid: true,
expected: 55 * constants.SompiPerKaspa,
},
// Negative tests.
{
name: "not-a-number",
amount: math.NaN(),
valid: false,
},
{
name: "-infinity",
amount: math.Inf(-1),
valid: false,
},
{
name: "+infinity",
amount: math.Inf(1),
valid: false,
},
}
for _, test := range tests {
a, err := NewAmount(test.amount)
switch {
case test.valid && err != nil:
t.Errorf("%v: Positive test Amount creation failed with: %v", test.name, err)
continue
case !test.valid && err == nil:
t.Errorf("%v: Negative test Amount creation succeeded (value %v) when should fail", test.name, a)
continue
}
if a != test.expected {
t.Errorf("%v: Created amount %v does not match expected %v", test.name, a, test.expected)
continue
}
}
}
func TestAmountUnitConversions(t *testing.T) {
tests := []struct {
name string
amount Amount
unit AmountUnit
converted float64
s string
}{
{
name: "MKAS",
amount: constants.MaxSompi,
unit: AmountMegaKAS,
converted: 21,
s: "21 MKAS",
},
{
name: "kKAS",
amount: 44433322211100,
unit: AmountKiloKAS,
converted: 444.33322211100,
s: "444.333222111 kKAS",
},
{
name: "KAS",
amount: 44433322211100,
unit: AmountKAS,
converted: 444333.22211100,
s: "444333.222111 KAS",
},
{
name: "mKAS",
amount: 44433322211100,
unit: AmountMilliKAS,
converted: 444333222.11100,
s: "444333222.111 mKAS",
},
{
name: "μKAS",
amount: 44433322211100,
unit: AmountMicroKAS,
converted: 444333222111.00,
s: "444333222111 μKAS",
},
{
name: "sompi",
amount: 44433322211100,
unit: AmountSompi,
converted: 44433322211100,
s: "44433322211100 Sompi",
},
{
name: "non-standard unit",
amount: 44433322211100,
unit: AmountUnit(-1),
converted: 4443332.2211100,
s: "4443332.22111 1e-1 KAS",
},
}
for _, test := range tests {
f := test.amount.ToUnit(test.unit)
if f != test.converted {
t.Errorf("%v: converted value %v does not match expected %v", test.name, f, test.converted)
continue
}
s := test.amount.Format(test.unit)
if s != test.s {
t.Errorf("%v: format '%v' does not match expected '%v'", test.name, s, test.s)
continue
}
// Verify that Amount.ToKAS works as advertised.
f1 := test.amount.ToUnit(AmountKAS)
f2 := test.amount.ToKAS()
if f1 != f2 {
t.Errorf("%v: ToKAS does not match ToUnit(AmountKAS): %v != %v", test.name, f1, f2)
}
// Verify that Amount.String works as advertised.
s1 := test.amount.Format(AmountKAS)
s2 := test.amount.String()
if s1 != s2 {
t.Errorf("%v: String does not match Format(AmountKAS): %v != %v", test.name, s1, s2)
}
}
}
func TestAmountMulF64(t *testing.T) {
tests := []struct {
name string
amt Amount
mul float64
res Amount
}{
{
name: "Multiply 0.1 KAS by 2",
amt: 100e5, // 0.1 KAS
mul: 2,
res: 200e5, // 0.2 KAS
},
{
name: "Multiply 0.2 KAS by 0.02",
amt: 200e5, // 0.2 KAS
mul: 1.02,
res: 204e5, // 0.204 KAS
},
{
name: "Round down",
amt: 49, // 49 Sompi
mul: 0.01,
res: 0,
},
{
name: "Round up",
amt: 50, // 50 Sompi
mul: 0.01,
res: 1, // 1 Sompi
},
{
name: "Multiply by 0.",
amt: 1e8, // 1 KAS
mul: 0,
res: 0, // 0 KAS
},
{
name: "Multiply 1 by 0.5.",
amt: 1, // 1 Sompi
mul: 0.5,
res: 1, // 1 Sompi
},
{
name: "Multiply 100 by 66%.",
amt: 100, // 100 Sompi
mul: 0.66,
res: 66, // 66 Sompi
},
{
name: "Multiply 100 by 66.6%.",
amt: 100, // 100 Sompi
mul: 0.666,
res: 67, // 67 Sompi
},
{
name: "Multiply 100 by 2/3.",
amt: 100, // 100 Sompi
mul: 2.0 / 3,
res: 67, // 67 Sompi
},
}
for _, test := range tests {
a := test.amt.MulF64(test.mul)
if a != test.res {
t.Errorf("%v: expected %v got %v", test.name, test.res, a)
}
}
}