mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-15 05:50:10 +00:00

* Add StagingArea struct * Implemented staging areas in blockStore * Move blockStagingShard to separate folder * Apply staging shard to acceptanceDataStore * Update blockHeaderStore with StagingArea * Add StagingArea to BlockRelationStore * Add StagingArea to blockStatusStore * Add StagingArea to consensusStateStore * Add StagingArea to daaBlocksStore * Add StagingArea to finalityStore * Add StagingArea to ghostdagDataStore * Add StagingArea to headersSelectedChainStore and headersSelectedTipStore * Add StagingArea to multisetStore * Add StagingArea to pruningStore * Add StagingArea to reachabilityDataStore * Add StagingArea to utxoDiffStore * Fix forgotten compilation error * Update reachability manager and some more things with StagingArea * Add StagingArea to dagTopologyManager, and some more * Add StagingArea to GHOSTDAGManager, and some more * Add StagingArea to difficultyManager, and some more * Add StagingArea to dagTraversalManager, and some more * Add StagingArea to headerTipsManager, and some more * Add StagingArea to constnsusStateManager, pastMedianTimeManager * Add StagingArea to transactionValidator * Add StagingArea to finalityManager * Add StagingArea to mergeDepthManager * Add StagingArea to pruningManager * Add StagingArea to rest of ValidateAndInsertBlock * Add StagingArea to blockValidator * Add StagingArea to coinbaseManager * Add StagingArea to syncManager * Add StagingArea to blockBuilder * Update consensus with StagingArea * Add StagingArea to ghostdag2 * Fix remaining compilation errors * Update names of stagingShards * Fix forgotten stagingArea passing * Mark stagingShard.isCommited = true once commited * Move isStaged to stagingShard, so that it's available without going through store * Make blockHeaderStore count be avilable from stagingShard * Fix remaining forgotten stagingArea passing * commitAllChanges should call dbTx.Commit in the end * Fix all tests tests in blockValidator * Fix all tests in consensusStateManager and some more * Fix all tests in pruningManager * Add many missing stagingAreas in tests * Fix many tests * Fix most of all other tests * Fix ghostdag_test.go * Add comment to StagingArea * Make list of StagingShards an array * Add comment to StagingShardID * Make sure all staging shards are pointer-receiver * Undo bucket rename in block_store * Typo: isCommited -> isCommitted * Add comment explaining why stagingArea.shards is an array
219 lines
6.9 KiB
Go
219 lines
6.9 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/pkg/errors"
|
|
)
|
|
|
|
type coinbaseManager struct {
|
|
subsidyReductionInterval uint64
|
|
baseSubsidy uint64
|
|
coinbasePayloadScriptPublicKeyMaxLength uint8
|
|
|
|
databaseContext model.DBReader
|
|
ghostdagDataStore model.GHOSTDAGDataStore
|
|
acceptanceDataStore model.AcceptanceDataStore
|
|
daaBlocksStore model.DAABlocksStore
|
|
}
|
|
|
|
func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
|
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
|
|
|
|
ghostdagData, err := c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
acceptanceData, err := c.acceptanceDataStore.Get(c.databaseContext, stagingArea, blockHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
daaAddedBlocksSet, err := c.daaAddedBlocksSet(stagingArea, blockHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
txOuts := make([]*externalapi.DomainTransactionOutput, 0, len(ghostdagData.MergeSetBlues()))
|
|
for i, blue := range ghostdagData.MergeSetBlues() {
|
|
txOut, hasReward, err := c.coinbaseOutputForBlueBlock(stagingArea, blue, acceptanceData[i], daaAddedBlocksSet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if hasReward {
|
|
txOuts = append(txOuts, txOut)
|
|
}
|
|
}
|
|
|
|
txOut, hasReward, err := c.coinbaseOutputForRewardFromRedBlocks(
|
|
stagingArea, ghostdagData, acceptanceData, daaAddedBlocksSet, coinbaseData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if hasReward {
|
|
txOuts = append(txOuts, txOut)
|
|
}
|
|
|
|
payload, err := c.serializeCoinbasePayload(ghostdagData.BlueScore(), coinbaseData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &externalapi.DomainTransaction{
|
|
Version: constants.MaxTransactionVersion,
|
|
Inputs: []*externalapi.DomainTransactionInput{},
|
|
Outputs: txOuts,
|
|
LockTime: 0,
|
|
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
|
Gas: 0,
|
|
Payload: payload,
|
|
}, 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) {
|
|
|
|
totalReward, err := c.calcMergedBlockReward(stagingArea, blueBlock, blockAcceptanceData, mergingBlockDAAAddedBlocksSet)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
if totalReward == 0 {
|
|
return nil, false, nil
|
|
}
|
|
|
|
// the ScriptPublicKey for the coinbase is parsed from the coinbase payload
|
|
_, coinbaseData, err := c.ExtractCoinbaseDataAndBlueScore(blockAcceptanceData.TransactionAcceptanceData[0].Transaction)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
txOut := &externalapi.DomainTransactionOutput{
|
|
Value: totalReward,
|
|
ScriptPublicKey: coinbaseData.ScriptPublicKey,
|
|
}
|
|
|
|
return txOut, true, nil
|
|
}
|
|
|
|
func (c *coinbaseManager) coinbaseOutputForRewardFromRedBlocks(stagingArea *model.StagingArea,
|
|
ghostdagData *model.BlockGHOSTDAGData, acceptanceData externalapi.AcceptanceData, daaAddedBlocksSet hashset.HashSet,
|
|
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransactionOutput, bool, error) {
|
|
|
|
totalReward := uint64(0)
|
|
mergeSetBluesCount := len(ghostdagData.MergeSetBlues())
|
|
for i, red := range ghostdagData.MergeSetReds() {
|
|
reward, err := c.calcMergedBlockReward(stagingArea, red, acceptanceData[mergeSetBluesCount+i], 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
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// The subsidy is halved every SubsidyReductionInterval blocks. Mathematically
|
|
// this is: baseSubsidy / 2^(blueScore/SubsidyReductionInterval)
|
|
//
|
|
// At the target block generation rate for the main network, this is
|
|
// approximately every 4 years.
|
|
func (c *coinbaseManager) calcBlockSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (uint64, error) {
|
|
if c.subsidyReductionInterval == 0 {
|
|
return c.baseSubsidy, nil
|
|
}
|
|
|
|
daaScore, err := c.daaBlocksStore.DAAScore(c.databaseContext, stagingArea, blockHash)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Equivalent to: baseSubsidy / 2^(daaScore/subsidyHalvingInterval)
|
|
return c.baseSubsidy >> uint(daaScore/c.subsidyReductionInterval), nil
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
subsidy, err := c.calcBlockSubsidy(stagingArea, blockHash)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return subsidy + totalFees, nil
|
|
}
|
|
|
|
// New instantiates a new CoinbaseManager
|
|
func New(
|
|
databaseContext model.DBReader,
|
|
|
|
subsidyReductionInterval uint64,
|
|
baseSubsidy uint64,
|
|
coinbasePayloadScriptPublicKeyMaxLength uint8,
|
|
|
|
ghostdagDataStore model.GHOSTDAGDataStore,
|
|
acceptanceDataStore model.AcceptanceDataStore,
|
|
daaBlocksStore model.DAABlocksStore) model.CoinbaseManager {
|
|
|
|
return &coinbaseManager{
|
|
databaseContext: databaseContext,
|
|
|
|
subsidyReductionInterval: subsidyReductionInterval,
|
|
baseSubsidy: baseSubsidy,
|
|
coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
|
|
|
|
ghostdagDataStore: ghostdagDataStore,
|
|
acceptanceDataStore: acceptanceDataStore,
|
|
daaBlocksStore: daaBlocksStore,
|
|
}
|
|
}
|