Ori Newman 27fdbd9c88
Upgrade to go 1.19 (#2191)
* Upgrade to go 1.19

* fmt
2023-02-27 10:03:11 +02:00

314 lines
14 KiB
Go

package coinbasemanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
"github.com/kaspanet/kaspad/infrastructure/db/database"
"github.com/pkg/errors"
"math"
)
type coinbaseManager struct {
subsidyGenesisReward uint64
preDeflationaryPhaseBaseSubsidy uint64
coinbasePayloadScriptPublicKeyMaxLength uint8
genesisHash *externalapi.DomainHash
deflationaryPhaseDaaScore uint64
deflationaryPhaseBaseSubsidy uint64
databaseContext model.DBReader
dagTraversalManager model.DAGTraversalManager
ghostdagDataStore model.GHOSTDAGDataStore
acceptanceDataStore model.AcceptanceDataStore
daaBlocksStore model.DAABlocksStore
blockStore model.BlockStore
pruningStore model.PruningStore
blockHeaderStore model.BlockHeaderStore
}
func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
coinbaseData *externalapi.DomainCoinbaseData) (expectedTransaction *externalapi.DomainTransaction, hasRedReward bool, err error) {
ghostdagData, err := c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, true)
if !database.IsNotFoundError(err) && err != nil {
return nil, false, err
}
// If there's ghostdag data with trusted data we prefer it because we need the original merge set non-pruned merge set.
if database.IsNotFoundError(err) {
ghostdagData, err = c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, false)
if err != nil {
return nil, false, err
}
}
acceptanceData, err := c.acceptanceDataStore.Get(c.databaseContext, stagingArea, blockHash)
if err != nil {
return nil, false, err
}
daaAddedBlocksSet, err := c.daaAddedBlocksSet(stagingArea, blockHash)
if err != nil {
return nil, false, err
}
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues()))
acceptanceDataMap := acceptanceDataFromArrayToMap(acceptanceData)
for _, blue := range ghostdagData.MergeSetBlues() {
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(stagingArea, blue, acceptanceDataMap[*blue], daaAddedBlocksSet)
if err != nil {
return nil, false, err
}
if hasReward {
txOuts = append(txOuts, txOut)
}
}
txOut, hasRedReward, err := c.coinbaseOutputForRewardFromRedBlocks(
stagingArea, ghostdagData, acceptanceData, daaAddedBlocksSet, coinbaseData)
if err != nil {
return nil, false, err
}
if hasRedReward {
txOuts = append(txOuts, txOut)
}
subsidy, err := c.CalcBlockSubsidy(stagingArea, blockHash)
if err != nil {
return nil, false, err
}
payload, err := c.serializeCoinbasePayload(ghostdagData.BlueScore(), coinbaseData, subsidy)
if err != nil {
return nil, false, err
}
return &externalapi.DomainTransaction{
Version: constants.MaxTransactionVersion,
Inputs: []*externalapi.DomainTransactionInput{},
Outputs: txOuts,
LockTime: 0,
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
Gas: 0,
Payload: payload,
}, hasRedReward, nil
}
func (c *coinbaseManager) daaAddedBlocksSet(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (
hashset.HashSet, error) {
daaAddedBlocks, err := c.daaBlocksStore.DAAAddedBlocks(c.databaseContext, stagingArea, blockHash)
if err != nil {
return nil, err
}
return hashset.NewFromSlice(daaAddedBlocks...), nil
}
// coinbaseOutputForBlueBlock calculates the output that should go into the coinbase transaction of blueBlock
// If blueBlock gets no fee - returns nil for txOut
func (c *coinbaseManager) coinbaseOutputForBlueBlock(stagingArea *model.StagingArea,
blueBlock *externalapi.DomainHash, blockAcceptanceData *externalapi.BlockAcceptanceData,
mergingBlockDAAAddedBlocksSet hashset.HashSet) (*externalapi.DomainTransactionOutput, bool, error) {
blockReward, err := c.calcMergedBlockReward(stagingArea, blueBlock, blockAcceptanceData, mergingBlockDAAAddedBlocksSet)
if err != nil {
return nil, false, err
}
if blockReward == 0 {
return nil, false, nil
}
// the ScriptPublicKey for the coinbase is parsed from the coinbase payload
_, coinbaseData, _, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(blockAcceptanceData.TransactionAcceptanceData[0].Transaction)
if err != nil {
return nil, false, err
}
txOut := &externalapi.DomainTransactionOutput{
Value: blockReward,
ScriptPublicKey: coinbaseData.ScriptPublicKey,
}
return txOut, true, nil
}
func (c *coinbaseManager) coinbaseOutputForRewardFromRedBlocks(stagingArea *model.StagingArea,
ghostdagData *externalapi.BlockGHOSTDAGData, acceptanceData externalapi.AcceptanceData, daaAddedBlocksSet hashset.HashSet,
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransactionOutput, bool, error) {
acceptanceDataMap := acceptanceDataFromArrayToMap(acceptanceData)
totalReward := uint64(0)
for _, red := range ghostdagData.MergeSetReds() {
reward, err := c.calcMergedBlockReward(stagingArea, red, acceptanceDataMap[*red], daaAddedBlocksSet)
if err != nil {
return nil, false, err
}
totalReward += reward
}
if totalReward == 0 {
return nil, false, nil
}
return &externalapi.DomainTransactionOutput{
Value: totalReward,
ScriptPublicKey: coinbaseData.ScriptPublicKey,
}, true, nil
}
func acceptanceDataFromArrayToMap(acceptanceData externalapi.AcceptanceData) map[externalapi.DomainHash]*externalapi.BlockAcceptanceData {
acceptanceDataMap := make(map[externalapi.DomainHash]*externalapi.BlockAcceptanceData, len(acceptanceData))
for _, blockAcceptanceData := range acceptanceData {
acceptanceDataMap[*blockAcceptanceData.BlockHash] = blockAcceptanceData
}
return acceptanceDataMap
}
// CalcBlockSubsidy returns the subsidy amount a block at the provided blue score
// should have. This is mainly used for determining how much the coinbase for
// newly generated blocks awards as well as validating the coinbase for blocks
// has the expected value.
func (c *coinbaseManager) CalcBlockSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (uint64, error) {
if blockHash.Equal(c.genesisHash) {
return c.subsidyGenesisReward, nil
}
blockDaaScore, err := c.daaBlocksStore.DAAScore(c.databaseContext, stagingArea, blockHash)
if err != nil {
return 0, err
}
if blockDaaScore < c.deflationaryPhaseDaaScore {
return c.preDeflationaryPhaseBaseSubsidy, nil
}
blockSubsidy := c.calcDeflationaryPeriodBlockSubsidy(blockDaaScore)
return blockSubsidy, nil
}
func (c *coinbaseManager) calcDeflationaryPeriodBlockSubsidy(blockDaaScore uint64) uint64 {
// We define a year as 365.25 days and a month as 365.25 / 12 = 30.4375
// secondsPerMonth = 30.4375 * 24 * 60 * 60
const secondsPerMonth = 2629800
// Note that this calculation implicitly assumes that block per second = 1 (by assuming daa score diff is in second units).
monthsSinceDeflationaryPhaseStarted := (blockDaaScore - c.deflationaryPhaseDaaScore) / secondsPerMonth
// Return the pre-calculated value from subsidy-per-month table
return c.getDeflationaryPeriodBlockSubsidyFromTable(monthsSinceDeflationaryPhaseStarted)
}
/*
This table was pre-calculated by calling `calcDeflationaryPeriodBlockSubsidyFloatCalc` for all months until reaching 0 subsidy.
To regenerate this table, run `TestBuildSubsidyTable` in coinbasemanager_test.go (note the `deflationaryPhaseBaseSubsidy` therein)
*/
var subsidyByDeflationaryMonthTable = []uint64{
44000000000, 41530469757, 39199543598, 36999442271, 34922823143, 32962755691, 31112698372, 29366476791, 27718263097, 26162556530, 24694165062, 23308188075, 22000000000, 20765234878, 19599771799, 18499721135, 17461411571, 16481377845, 15556349186, 14683238395, 13859131548, 13081278265, 12347082531, 11654094037, 11000000000,
10382617439, 9799885899, 9249860567, 8730705785, 8240688922, 7778174593, 7341619197, 6929565774, 6540639132, 6173541265, 5827047018, 5500000000, 5191308719, 4899942949, 4624930283, 4365352892, 4120344461, 3889087296, 3670809598, 3464782887, 3270319566, 3086770632, 2913523509, 2750000000, 2595654359,
2449971474, 2312465141, 2182676446, 2060172230, 1944543648, 1835404799, 1732391443, 1635159783, 1543385316, 1456761754, 1375000000, 1297827179, 1224985737, 1156232570, 1091338223, 1030086115, 972271824, 917702399, 866195721, 817579891, 771692658, 728380877, 687500000, 648913589, 612492868,
578116285, 545669111, 515043057, 486135912, 458851199, 433097860, 408789945, 385846329, 364190438, 343750000, 324456794, 306246434, 289058142, 272834555, 257521528, 243067956, 229425599, 216548930, 204394972, 192923164, 182095219, 171875000, 162228397, 153123217, 144529071,
136417277, 128760764, 121533978, 114712799, 108274465, 102197486, 96461582, 91047609, 85937500, 81114198, 76561608, 72264535, 68208638, 64380382, 60766989, 57356399, 54137232, 51098743, 48230791, 45523804, 42968750, 40557099, 38280804, 36132267, 34104319,
32190191, 30383494, 28678199, 27068616, 25549371, 24115395, 22761902, 21484375, 20278549, 19140402, 18066133, 17052159, 16095095, 15191747, 14339099, 13534308, 12774685, 12057697, 11380951, 10742187, 10139274, 9570201, 9033066, 8526079, 8047547,
7595873, 7169549, 6767154, 6387342, 6028848, 5690475, 5371093, 5069637, 4785100, 4516533, 4263039, 4023773, 3797936, 3584774, 3383577, 3193671, 3014424, 2845237, 2685546, 2534818, 2392550, 2258266, 2131519, 2011886, 1898968,
1792387, 1691788, 1596835, 1507212, 1422618, 1342773, 1267409, 1196275, 1129133, 1065759, 1005943, 949484, 896193, 845894, 798417, 753606, 711309, 671386, 633704, 598137, 564566, 532879, 502971, 474742, 448096,
422947, 399208, 376803, 355654, 335693, 316852, 299068, 282283, 266439, 251485, 237371, 224048, 211473, 199604, 188401, 177827, 167846, 158426, 149534, 141141, 133219, 125742, 118685, 112024, 105736,
99802, 94200, 88913, 83923, 79213, 74767, 70570, 66609, 62871, 59342, 56012, 52868, 49901, 47100, 44456, 41961, 39606, 37383, 35285, 33304, 31435, 29671, 28006, 26434, 24950,
23550, 22228, 20980, 19803, 18691, 17642, 16652, 15717, 14835, 14003, 13217, 12475, 11775, 11114, 10490, 9901, 9345, 8821, 8326, 7858, 7417, 7001, 6608, 6237, 5887,
5557, 5245, 4950, 4672, 4410, 4163, 3929, 3708, 3500, 3304, 3118, 2943, 2778, 2622, 2475, 2336, 2205, 2081, 1964, 1854, 1750, 1652, 1559, 1471, 1389,
1311, 1237, 1168, 1102, 1040, 982, 927, 875, 826, 779, 735, 694, 655, 618, 584, 551, 520, 491, 463, 437, 413, 389, 367, 347, 327,
309, 292, 275, 260, 245, 231, 218, 206, 194, 183, 173, 163, 154, 146, 137, 130, 122, 115, 109, 103, 97, 91, 86, 81, 77,
73, 68, 65, 61, 57, 54, 51, 48, 45, 43, 40, 38, 36, 34, 32, 30, 28, 27, 25, 24, 22, 21, 20, 19, 18,
17, 16, 15, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4,
4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0,
}
func (c *coinbaseManager) getDeflationaryPeriodBlockSubsidyFromTable(month uint64) uint64 {
if month >= uint64(len(subsidyByDeflationaryMonthTable)) {
month = uint64(len(subsidyByDeflationaryMonthTable) - 1)
}
return subsidyByDeflationaryMonthTable[month]
}
func (c *coinbaseManager) calcDeflationaryPeriodBlockSubsidyFloatCalc(month uint64) uint64 {
baseSubsidy := c.deflationaryPhaseBaseSubsidy
subsidy := float64(baseSubsidy) / math.Pow(2, float64(month)/12)
return uint64(subsidy)
}
func (c *coinbaseManager) calcMergedBlockReward(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
blockAcceptanceData *externalapi.BlockAcceptanceData, mergingBlockDAAAddedBlocksSet hashset.HashSet) (uint64, error) {
if !blockHash.Equal(blockAcceptanceData.BlockHash) {
return 0, errors.Errorf("blockAcceptanceData.BlockHash is expected to be %s but got %s",
blockHash, blockAcceptanceData.BlockHash)
}
if !mergingBlockDAAAddedBlocksSet.Contains(blockHash) {
return 0, nil
}
totalFees := uint64(0)
for _, txAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
if txAcceptanceData.IsAccepted {
totalFees += txAcceptanceData.Fee
}
}
block, err := c.blockStore.Block(c.databaseContext, stagingArea, blockHash)
if err != nil {
return 0, err
}
_, _, subsidy, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex])
if err != nil {
return 0, err
}
return subsidy + totalFees, nil
}
// New instantiates a new CoinbaseManager
func New(
databaseContext model.DBReader,
subsidyGenesisReward uint64,
preDeflationaryPhaseBaseSubsidy uint64,
coinbasePayloadScriptPublicKeyMaxLength uint8,
genesisHash *externalapi.DomainHash,
deflationaryPhaseDaaScore uint64,
deflationaryPhaseBaseSubsidy uint64,
dagTraversalManager model.DAGTraversalManager,
ghostdagDataStore model.GHOSTDAGDataStore,
acceptanceDataStore model.AcceptanceDataStore,
daaBlocksStore model.DAABlocksStore,
blockStore model.BlockStore,
pruningStore model.PruningStore,
blockHeaderStore model.BlockHeaderStore) model.CoinbaseManager {
return &coinbaseManager{
databaseContext: databaseContext,
subsidyGenesisReward: subsidyGenesisReward,
preDeflationaryPhaseBaseSubsidy: preDeflationaryPhaseBaseSubsidy,
coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
genesisHash: genesisHash,
deflationaryPhaseDaaScore: deflationaryPhaseDaaScore,
deflationaryPhaseBaseSubsidy: deflationaryPhaseBaseSubsidy,
dagTraversalManager: dagTraversalManager,
ghostdagDataStore: ghostdagDataStore,
acceptanceDataStore: acceptanceDataStore,
daaBlocksStore: daaBlocksStore,
blockStore: blockStore,
pruningStore: pruningStore,
blockHeaderStore: blockHeaderStore,
}
}