diff --git a/blockchain/chain.go b/blockchain/chain.go index 3106547f..d2219c3c 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -12,6 +12,7 @@ import ( "time" "github.com/decred/dcrd/blockchain/stake" + "github.com/decred/dcrd/blockchain/standalone" "github.com/decred/dcrd/chaincfg" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/database" @@ -150,7 +151,7 @@ type BlockChain struct { // subsidyCache is the cache that provides quick lookup of subsidy // values. - subsidyCache *SubsidyCache + subsidyCache *standalone.SubsidyCache // chainLock protects concurrent access to the vast majority of the // fields in this struct below this point. @@ -749,7 +750,7 @@ func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block, curTotalTxns := b.stateSnapshot.TotalTxns curTotalSubsidy := b.stateSnapshot.TotalSubsidy b.stateLock.RUnlock() - subsidy := CalculateAddedSubsidy(block, parent) + subsidy := calculateAddedSubsidy(block, parent) numTxns := uint64(len(block.Transactions()) + len(block.STransactions())) blockSize := uint64(block.MsgBlock().Header.Size) state := newBestState(node, blockSize, numTxns, curTotalTxns+numTxns, @@ -921,7 +922,7 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block, parent *dcrutil.Blo numParentTxns := uint64(len(parent.Transactions()) + len(parent.STransactions())) numBlockTxns := uint64(len(block.Transactions()) + len(block.STransactions())) newTotalTxns := curTotalTxns - numBlockTxns - subsidy := CalculateAddedSubsidy(block, parent) + subsidy := calculateAddedSubsidy(block, parent) newTotalSubsidy := curTotalSubsidy - subsidy prevNode := node.parent state := newBestState(prevNode, parentBlockSize, numParentTxns, @@ -2129,7 +2130,7 @@ func New(config *Config) (*BlockChain, error) { } } - b.subsidyCache = NewSubsidyCache(b.bestChain.Tip().height, b.chainParams) + b.subsidyCache = standalone.NewSubsidyCache(&subsidyParams{b.chainParams}) b.pruner = newChainPruner(&b) // The version 5 database upgrade requires a full reindex. Perform, or diff --git a/blockchain/subsidy.go b/blockchain/subsidy.go index 4263c2a3..a6ca4d3b 100644 --- a/blockchain/subsidy.go +++ b/blockchain/subsidy.go @@ -8,9 +8,9 @@ package blockchain import ( "bytes" "fmt" - "sync" "github.com/decred/dcrd/blockchain/stake" + "github.com/decred/dcrd/blockchain/standalone" "github.com/decred/dcrd/chaincfg" "github.com/decred/dcrd/dcrutil" "github.com/decred/dcrd/txscript" @@ -24,191 +24,126 @@ const subsidyCacheInitWidth = 4 // SubsidyCache is a structure that caches calculated values of subsidy so that // they're not constantly recalculated. The blockchain struct itself possesses a // pointer to a preinitialized SubsidyCache. -type SubsidyCache struct { - subsidyCache map[uint64]int64 - subsidyCacheLock sync.RWMutex +// +// Deprecated: Use standalone.SubsidyCache instead. +type SubsidyCache = standalone.SubsidyCache - params *chaincfg.Params +// subsidyParams adapts v1 chaincfg.Params to implement the +// standalone.SubsidyParams interface. It is already implemented by the v2 +// chaincfg.Params, but updating to those requires a major version bump since +// the type is used in the public API. +type subsidyParams struct { + *chaincfg.Params +} + +// BaseSubsidyValue returns the starting base max potential subsidy amount for +// mined blocks. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) BaseSubsidyValue() int64 { + return p.BaseSubsidy +} + +// SubsidyReductionMultiplier returns the multiplier to use when performing the +// exponential subsidy reduction. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) SubsidyReductionMultiplier() int64 { + return p.MulSubsidy +} + +// SubsidyReductionDivisor returns the divisor to use when performing the +// exponential subsidy reduction. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) SubsidyReductionDivisor() int64 { + return p.DivSubsidy +} + +// SubsidyReductionIntervalBlocks returns the reduction interval in number of +// blocks. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) SubsidyReductionIntervalBlocks() int64 { + return p.SubsidyReductionInterval +} + +// WorkSubsidyProportion returns the comparative proportion of the subsidy +// generated for creating a block (PoW). +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) WorkSubsidyProportion() uint16 { + return p.WorkRewardProportion +} + +// StakeSubsidyProportion returns the comparative proportion of the subsidy +// generated for casting stake votes (collectively, per block). +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) StakeSubsidyProportion() uint16 { + return p.StakeRewardProportion +} + +// TreasurySubsidyProportion returns the comparative proportion of the subsidy +// allocated to the project treasury. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) TreasurySubsidyProportion() uint16 { + return p.BlockTaxProportion +} + +// VotesPerBlock returns the maximum number of votes a block must contain to +// receive full subsidy. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) VotesPerBlock() uint16 { + return p.TicketsPerBlock +} + +// StakeValidationBeginHeight returns the height at which votes become required +// to extend a block. This height is the first that will be voted on, but will +// not include any votes itself. +// +// This is part of the standalone.SubsidyParams interface. +func (p *subsidyParams) StakeValidationBeginHeight() int64 { + return p.StakeValidationHeight } // NewSubsidyCache initializes a new subsidy cache for a given height. It // precalculates the values of the subsidy that are most likely to be seen by // the client when it connects to the network. +// +// Deprecated: Use standalone.NewSubsidyCache instead. func NewSubsidyCache(height int64, params *chaincfg.Params) *SubsidyCache { - scm := make(map[uint64]int64) - sc := SubsidyCache{ - subsidyCache: scm, - params: params, - } - - iteration := uint64(height / params.SubsidyReductionInterval) - if iteration < subsidyCacheInitWidth { - return &sc - } - - for i := iteration - 4; i <= iteration; i++ { - sc.CalcBlockSubsidy(int64(iteration) * params.SubsidyReductionInterval) - } - - return &sc -} - -// CalcBlockSubsidy returns the subsidy amount a block at the provided height -// 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. -// -// Subsidy calculation for exponential reductions: -// 0 for i in range (0, height / SubsidyReductionInterval): -// 1 subsidy *= MulSubsidy -// 2 subsidy /= DivSubsidy -// -// Safe for concurrent access. -func (s *SubsidyCache) CalcBlockSubsidy(height int64) int64 { - // Block height 1 subsidy is 'special' and used to - // distribute initial tokens, if any. - if height == 1 { - return s.params.BlockOneSubsidy() - } - - iteration := uint64(height / s.params.SubsidyReductionInterval) - - if iteration == 0 { - return s.params.BaseSubsidy - } - - // First, check the cache. - s.subsidyCacheLock.RLock() - cachedValue, existsInCache := s.subsidyCache[iteration] - s.subsidyCacheLock.RUnlock() - if existsInCache { - return cachedValue - } - - // If a cached block subsidy for the provided height is not found - // fetch the last cached block subsidy and generate the requested - // subsidy from that. - var lastCachedIter uint64 - s.subsidyCacheLock.RLock() - for k := iteration - 1; k > 0; k-- { - cachedValue, existsInCache = s.subsidyCache[k] - if existsInCache { - lastCachedIter = k - break - } - } - s.subsidyCacheLock.RUnlock() - - if lastCachedIter != 0 { - // Calculate the requested block subsidy using the last cached - // block subsidy. - diff := iteration - lastCachedIter - for i := uint64(0); i < diff; i++ { - cachedValue *= s.params.MulSubsidy - cachedValue /= s.params.DivSubsidy - } - - s.subsidyCacheLock.Lock() - s.subsidyCache[iteration] = cachedValue - s.subsidyCacheLock.Unlock() - - return cachedValue - } - - // Calculate the subsidy from scratch and store in the cache. - subsidy := s.params.BaseSubsidy - for i := uint64(0); i < iteration; i++ { - subsidy *= s.params.MulSubsidy - subsidy /= s.params.DivSubsidy - } - - s.subsidyCacheLock.Lock() - s.subsidyCache[iteration] = subsidy - s.subsidyCacheLock.Unlock() - - return subsidy + return standalone.NewSubsidyCache(&subsidyParams{params}) } // CalcBlockWorkSubsidy calculates the proof of work subsidy for a block as a // proportion of the total subsidy. +// +// Deprecated: Use standalone.SubsidyCache.CalcWorkSubsidy instead. func CalcBlockWorkSubsidy(subsidyCache *SubsidyCache, height int64, voters uint16, params *chaincfg.Params) int64 { - subsidy := subsidyCache.CalcBlockSubsidy(height) - - proportionWork := int64(params.WorkRewardProportion) - proportions := int64(params.TotalSubsidyProportions()) - subsidy *= proportionWork - subsidy /= proportions - - // Ignore the voters field of the header before we're at a point - // where there are any voters. - if height < params.StakeValidationHeight { - return subsidy - } - - // If there are no voters, subsidy is 0. The block will fail later anyway. - if voters == 0 { - return 0 - } - - // Adjust for the number of voters. This shouldn't ever overflow if you start - // with 50 * 10^8 Atoms and voters and potentialVoters are uint16. - potentialVoters := params.TicketsPerBlock - actual := (int64(voters) * subsidy) / int64(potentialVoters) - - return actual + return subsidyCache.CalcWorkSubsidy(height, voters) } // CalcStakeVoteSubsidy calculates the subsidy for a stake vote based on the height // of its input SStx. // // Safe for concurrent access. +// +// Deprecated: Use standalone.SubsidyCache.CalcStakeVoteSubsidy instead. func CalcStakeVoteSubsidy(subsidyCache *SubsidyCache, height int64, params *chaincfg.Params) int64 { - // Calculate the actual reward for this block, then further reduce reward - // proportional to StakeRewardProportion. - // Note that voters/potential voters is 1, so that vote reward is calculated - // irrespective of block reward. - subsidy := subsidyCache.CalcBlockSubsidy(height) - - proportionStake := int64(params.StakeRewardProportion) - proportions := int64(params.TotalSubsidyProportions()) - subsidy *= proportionStake - subsidy /= (proportions * int64(params.TicketsPerBlock)) - - return subsidy + return subsidyCache.CalcStakeVoteSubsidy(height) } // CalcBlockTaxSubsidy calculates the subsidy for the organization address in the // coinbase. // // Safe for concurrent access. +// +// Deprecated: Use standalone.SubsidyCache.CalcTreasurySubsidy instead. func CalcBlockTaxSubsidy(subsidyCache *SubsidyCache, height int64, voters uint16, params *chaincfg.Params) int64 { - if params.BlockTaxProportion == 0 { - return 0 - } - - subsidy := subsidyCache.CalcBlockSubsidy(height) - - proportionTax := int64(params.BlockTaxProportion) - proportions := int64(params.TotalSubsidyProportions()) - subsidy *= proportionTax - subsidy /= proportions - - // Assume all voters 'present' before stake voting is turned on. - if height < params.StakeValidationHeight { - voters = 5 - } - - // If there are no voters, subsidy is 0. The block will fail later anyway. - if voters == 0 && height >= params.StakeValidationHeight { - return 0 - } - - // Adjust for the number of voters. This shouldn't ever overflow if you start - // with 50 * 10^8 Atoms and voters and potentialVoters are uint16. - potentialVoters := params.TicketsPerBlock - adjusted := (int64(voters) * subsidy) / int64(potentialVoters) - - return adjusted + return subsidyCache.CalcTreasurySubsidy(height, voters) } // blockOneCoinbasePaysTokens checks to see if the first block coinbase pays @@ -295,58 +230,66 @@ func blockOneCoinbasePaysTokens(tx *dcrutil.Tx, params *chaincfg.Params) error { // BlockOneCoinbasePaysTokens checks to see if the first block coinbase pays // out to the network initial token ledger. // -// DEPRECATED. This will be removed in the next major version bump. +// Deprecated: This will be removed in the next major version bump. func BlockOneCoinbasePaysTokens(tx *dcrutil.Tx, params *chaincfg.Params) error { return blockOneCoinbasePaysTokens(tx, params) } -// CoinbasePaysTax checks to see if a given block's coinbase correctly pays -// tax to the developer organization. -func CoinbasePaysTax(subsidyCache *SubsidyCache, tx *dcrutil.Tx, height int64, voters uint16, params *chaincfg.Params) error { - // Taxes only apply from block 2 onwards. +// coinbasePaysTreasury checks to see if a given block's coinbase correctly pays +// the treasury. +func coinbasePaysTreasury(subsidyCache *standalone.SubsidyCache, tx *dcrutil.Tx, height int64, voters uint16, params *chaincfg.Params) error { + // Treasury subsidy only applies from block 2 onwards. if height <= 1 { return nil } - // Tax is disabled. + // Treasury subsidy is disabled. if params.BlockTaxProportion == 0 { return nil } if len(tx.MsgTx().TxOut) == 0 { - errStr := fmt.Sprintf("invalid coinbase (no outputs)") - return ruleError(ErrNoTxOutputs, errStr) + str := fmt.Sprintf("invalid coinbase (no outputs)") + return ruleError(ErrNoTxOutputs, str) } - taxOutput := tx.MsgTx().TxOut[0] - if taxOutput.Version != params.OrganizationPkScriptVersion { - return ruleError(ErrNoTax, - "coinbase tax output uses incorrect script version") + treasuryOutput := tx.MsgTx().TxOut[0] + if treasuryOutput.Version != params.OrganizationPkScriptVersion { + str := fmt.Sprintf("treasury output version %d is instead of %d", + treasuryOutput.Version, params.OrganizationPkScriptVersion) + return ruleError(ErrNoTax, str) } - if !bytes.Equal(taxOutput.PkScript, params.OrganizationPkScript) { - return ruleError(ErrNoTax, - "coinbase tax output script does not match the "+ - "required script") + if !bytes.Equal(treasuryOutput.PkScript, params.OrganizationPkScript) { + str := fmt.Sprintf("treasury output script is %x instead of %x", + treasuryOutput.PkScript, params.OrganizationPkScript) + return ruleError(ErrNoTax, str) } - // Get the amount of subsidy that should have been paid out to - // the organization, then check it. - orgSubsidy := CalcBlockTaxSubsidy(subsidyCache, height, voters, params) - if orgSubsidy != taxOutput.Value { - errStr := fmt.Sprintf("amount in output 0 has non matching org "+ - "calculated amount; got %v, want %v", taxOutput.Value, - orgSubsidy) - return ruleError(ErrNoTax, errStr) + // Calculate the amount of subsidy that should have been paid out to the + // Treasury and ensure the subsidy generated is correct. + orgSubsidy := subsidyCache.CalcTreasurySubsidy(height, voters) + if orgSubsidy != treasuryOutput.Value { + str := fmt.Sprintf("treasury output amount is %s instead of %s", + dcrutil.Amount(treasuryOutput.Value), dcrutil.Amount(orgSubsidy)) + return ruleError(ErrNoTax, str) } return nil } -// CalculateAddedSubsidy calculates the amount of subsidy added by a block +// CoinbasePaysTax checks to see if a given block's coinbase correctly pays +// tax to the developer organization. +// +// Deprecated: This will be removed in the next major version. +func CoinbasePaysTax(subsidyCache *SubsidyCache, tx *dcrutil.Tx, height int64, voters uint16, params *chaincfg.Params) error { + return coinbasePaysTreasury(subsidyCache, tx, height, voters, params) +} + +// calculateAddedSubsidy calculates the amount of subsidy added by a block // and its parent. The blocks passed to this function MUST be valid blocks // that have already been confirmed to abide by the consensus rules of the // network, or the function might panic. -func CalculateAddedSubsidy(block, parent *dcrutil.Block) int64 { +func calculateAddedSubsidy(block, parent *dcrutil.Block) int64 { var subsidy int64 if headerApprovesParent(&block.MsgBlock().Header) { subsidy += parent.MsgBlock().Transactions[0].TxIn[0].ValueIn @@ -360,3 +303,13 @@ func CalculateAddedSubsidy(block, parent *dcrutil.Block) int64 { return subsidy } + +// CalculateAddedSubsidy calculates the amount of subsidy added by a block +// and its parent. The blocks passed to this function MUST be valid blocks +// that have already been confirmed to abide by the consensus rules of the +// network, or the function might panic. +// +// Deprecated: This will no longer be exported in the next major version. +func CalculateAddedSubsidy(block, parent *dcrutil.Block) int64 { + return calculateAddedSubsidy(block, parent) +} diff --git a/blockchain/subsidy_test.go b/blockchain/subsidy_test.go index 61a91eff..38f239cd 100644 --- a/blockchain/subsidy_test.go +++ b/blockchain/subsidy_test.go @@ -13,41 +13,52 @@ import ( func TestBlockSubsidy(t *testing.T) { mainnet := &chaincfg.MainNetParams - subsidyCache := NewSubsidyCache(0, mainnet) + reductionInterval := mainnet.SubsidyReductionInterval + stakeValidationHeight := mainnet.StakeValidationHeight + votesPerBlock := mainnet.TicketsPerBlock + // subsidySum returns the sum of the individual subsidy types for the given + // height. Note that this value is not exactly the same as the full subsidy + // originally used to calculate the individual proportions due to the use + // of integer math. + cache := NewSubsidyCache(0, mainnet) + subsidySum := func(height int64) int64 { + work := CalcBlockWorkSubsidy(cache, height, votesPerBlock, mainnet) + vote := CalcStakeVoteSubsidy(cache, height, mainnet) * int64(votesPerBlock) + treasury := CalcBlockTaxSubsidy(cache, height, votesPerBlock, mainnet) + return work + vote + treasury + } + + // Calculate the total possible subsidy. totalSubsidy := mainnet.BlockOneSubsidy() - for i := int64(0); ; i++ { - // Genesis block or first block. - if i == 0 || i == 1 { + for reductionNum := int64(0); ; reductionNum++ { + // The first interval contains a few special cases: + // 1) Block 0 does not produce any subsidy + // 2) Block 1 consists of a special initial coin distribution + // 3) Votes do not produce subsidy until voting begins + if reductionNum == 0 { + // Account for the block up to the point voting begins ignoring the + // first two special blocks. + subsidyCalcHeight := int64(2) + nonVotingBlocks := stakeValidationHeight - subsidyCalcHeight + totalSubsidy += subsidySum(subsidyCalcHeight) * nonVotingBlocks + + // Account for the blocks remaining in the interval once voting + // begins. + subsidyCalcHeight = stakeValidationHeight + votingBlocks := reductionInterval - subsidyCalcHeight + totalSubsidy += subsidySum(subsidyCalcHeight) * votingBlocks continue } - if i%mainnet.SubsidyReductionInterval == 0 { - numBlocks := mainnet.SubsidyReductionInterval - // First reduction internal, which is reduction interval - 2 - // to skip the genesis block and block one. - if i == mainnet.SubsidyReductionInterval { - numBlocks -= 2 - } - height := i - numBlocks - - work := CalcBlockWorkSubsidy(subsidyCache, height, - mainnet.TicketsPerBlock, mainnet) - stake := CalcStakeVoteSubsidy(subsidyCache, height, - mainnet) * int64(mainnet.TicketsPerBlock) - tax := CalcBlockTaxSubsidy(subsidyCache, height, - mainnet.TicketsPerBlock, mainnet) - if (work + stake + tax) == 0 { - break - } - totalSubsidy += ((work + stake + tax) * numBlocks) - - // First reduction internal, subtract the stake subsidy for - // blocks before the staking system is enabled. - if i == mainnet.SubsidyReductionInterval { - totalSubsidy -= stake * (mainnet.StakeValidationHeight - 2) - } + // Account for the all other reduction intervals until all subsidy has + // been produced. + subsidyCalcHeight := reductionNum * reductionInterval + sum := subsidySum(subsidyCalcHeight) + if sum == 0 { + break } + totalSubsidy += sum * reductionInterval } if totalSubsidy != 2099999999800912 { diff --git a/blockchain/validate.go b/blockchain/validate.go index ecdc06d4..a2ed1b9d 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -1892,7 +1892,7 @@ func checkTicketRedeemerCommitments(ticketHash *chainhash.Hash, ticketOuts []*st // // NOTE: The caller MUST have already determined that the provided transaction // is a vote. -func checkVoteInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight int64, view *UtxoViewpoint, params *chaincfg.Params) error { +func checkVoteInputs(subsidyCache *standalone.SubsidyCache, tx *dcrutil.Tx, txHeight int64, view *UtxoViewpoint, params *chaincfg.Params) error { ticketMaturity := int64(params.TicketMaturity) voteHash := tx.Hash() msgTx := tx.MsgTx() @@ -1905,8 +1905,7 @@ func checkVoteInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight int64, // is voting on. Unfortunately, this is now part of consensus, so changing // it requires a hard fork vote. _, heightVotingOn := stake.SSGenBlockVotedOn(msgTx) - voteSubsidy := CalcStakeVoteSubsidy(subsidyCache, int64(heightVotingOn), - params) + voteSubsidy := subsidyCache.CalcStakeVoteSubsidy(int64(heightVotingOn)) // The input amount specified by the stakebase must commit to the subsidy // generated by the vote. @@ -2099,7 +2098,7 @@ func checkRevocationInputs(tx *dcrutil.Tx, txHeight int64, view *UtxoViewpoint, // // NOTE: The transaction MUST have already been sanity checked with the // CheckTransactionSanity function prior to calling this function. -func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight int64, view *UtxoViewpoint, checkFraudProof bool, chainParams *chaincfg.Params) (int64, error) { +func CheckTransactionInputs(subsidyCache *standalone.SubsidyCache, tx *dcrutil.Tx, txHeight int64, view *UtxoViewpoint, checkFraudProof bool, chainParams *chaincfg.Params) (int64, error) { // Coinbase transactions have no inputs. msgTx := tx.MsgTx() if standalone.IsCoinBaseTx(msgTx) { @@ -2160,8 +2159,8 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight if isVote && idx == 0 { // However, do add the reward amount. _, heightVotingOn := stake.SSGenBlockVotedOn(msgTx) - stakeVoteSubsidy := CalcStakeVoteSubsidy(subsidyCache, - int64(heightVotingOn), chainParams) + stakeVoteSubsidy := subsidyCache.CalcStakeVoteSubsidy( + int64(heightVotingOn)) totalAtomIn += stakeVoteSubsidy continue } @@ -2568,7 +2567,7 @@ func checkNumSigOps(tx *dcrutil.Tx, view *UtxoViewpoint, index int, txTree bool, // checkStakeBaseAmounts calculates the total amount given as subsidy from // single stakebase transactions (votes) within a block. This function skips a // ton of checks already performed by CheckTransactionInputs. -func checkStakeBaseAmounts(subsidyCache *SubsidyCache, height int64, params *chaincfg.Params, txs []*dcrutil.Tx, view *UtxoViewpoint) error { +func checkStakeBaseAmounts(subsidyCache *standalone.SubsidyCache, height int64, params *chaincfg.Params, txs []*dcrutil.Tx, view *UtxoViewpoint) error { for _, tx := range txs { msgTx := tx.MsgTx() if stake.IsSSGen(msgTx) { @@ -2594,8 +2593,7 @@ func checkStakeBaseAmounts(subsidyCache *SubsidyCache, height int64, params *cha // Subsidy aligns with the height we're voting on, not // with the height of the current block. - calcSubsidy := CalcStakeVoteSubsidy(subsidyCache, - height-1, params) + calcSubsidy := subsidyCache.CalcStakeVoteSubsidy(height - 1) if difference > calcSubsidy { str := fmt.Sprintf("ssgen tx %v spent more "+ @@ -2644,7 +2642,7 @@ func getStakeBaseAmounts(txs []*dcrutil.Tx, view *UtxoViewpoint) (int64, error) // getStakeTreeFees determines the amount of fees for in the stake tx tree of // some node given a transaction store. -func getStakeTreeFees(subsidyCache *SubsidyCache, height int64, params *chaincfg.Params, txs []*dcrutil.Tx, view *UtxoViewpoint) (dcrutil.Amount, error) { +func getStakeTreeFees(subsidyCache *standalone.SubsidyCache, height int64, params *chaincfg.Params, txs []*dcrutil.Tx, view *UtxoViewpoint) (dcrutil.Amount, error) { totalInputs := int64(0) totalOutputs := int64(0) for _, tx := range txs { @@ -2680,8 +2678,7 @@ func getStakeTreeFees(subsidyCache *SubsidyCache, height int64, params *chaincfg if isSSGen { // Subsidy aligns with the height we're voting on, not // with the height of the current block. - totalOutputs -= CalcStakeVoteSubsidy(subsidyCache, - height-1, params) + totalOutputs -= subsidyCache.CalcStakeVoteSubsidy(height - 1) } } @@ -2698,7 +2695,7 @@ func getStakeTreeFees(subsidyCache *SubsidyCache, height int64, params *chaincfg // transaction inputs for a transaction list given a predetermined TxStore. // After ensuring the transaction is valid, the transaction is connected to the // UTXO viewpoint. TxTree true == Regular, false == Stake -func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inputFees dcrutil.Amount, node *blockNode, txs []*dcrutil.Tx, view *UtxoViewpoint, stxos *[]spentTxOut, txTree bool) error { +func (b *BlockChain) checkTransactionsAndConnect(inputFees dcrutil.Amount, node *blockNode, txs []*dcrutil.Tx, view *UtxoViewpoint, stxos *[]spentTxOut, txTree bool) error { // Perform several checks on the inputs for each transaction. Also // accumulate the total fees. This could technically be combined with // the loop above instead of running another loop over the @@ -2767,12 +2764,12 @@ func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inp var expAtomOut int64 if node.height == 1 { - expAtomOut = subsidyCache.CalcBlockSubsidy(node.height) + expAtomOut = b.subsidyCache.CalcBlockSubsidy(node.height) } else { - subsidyWork := CalcBlockWorkSubsidy(subsidyCache, - node.height, node.voters, b.chainParams) - subsidyTax := CalcBlockTaxSubsidy(subsidyCache, - node.height, node.voters, b.chainParams) + subsidyWork := b.subsidyCache.CalcWorkSubsidy(node.height, + node.voters) + subsidyTax := b.subsidyCache.CalcTreasurySubsidy(node.height, + node.voters) expAtomOut = subsidyWork + subsidyTax + totalFees } @@ -2806,7 +2803,7 @@ func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inp return ruleError(ErrNoStakeTx, str) } - err := checkStakeBaseAmounts(subsidyCache, node.height, + err := checkStakeBaseAmounts(b.subsidyCache, node.height, b.chainParams, txs, view) if err != nil { return err @@ -2821,8 +2818,7 @@ func (b *BlockChain) checkTransactionsAndConnect(subsidyCache *SubsidyCache, inp if node.height >= b.chainParams.StakeValidationHeight { // Subsidy aligns with the height we're voting on, not // with the height of the current block. - expAtomOut = CalcStakeVoteSubsidy(subsidyCache, - node.height-1, b.chainParams) * + expAtomOut = b.subsidyCache.CalcStakeVoteSubsidy(node.height-1) * int64(node.voters) } else { expAtomOut = totalFees @@ -2893,9 +2889,9 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B "of expected %v", view.BestHash(), parentHash)) } - // Check that the coinbase pays the tax, if applicable. - err := CoinbasePaysTax(b.subsidyCache, block.Transactions()[0], node.height, - node.voters, b.chainParams) + // Check that the coinbase pays the treasury, if applicable. + err := coinbasePaysTreasury(b.subsidyCache, block.Transactions()[0], + node.height, node.voters, b.chainParams) if err != nil { return err } @@ -2968,8 +2964,8 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B return err } - err = b.checkTransactionsAndConnect(b.subsidyCache, 0, node, - block.STransactions(), view, stxos, false) + err = b.checkTransactionsAndConnect(0, node, block.STransactions(), + view, stxos, false) if err != nil { log.Tracef("checkTransactionsAndConnect failed for "+ "TxTreeStake: %v", err) @@ -3027,7 +3023,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B return err } - err = b.checkTransactionsAndConnect(b.subsidyCache, stakeTreeFees, node, + err = b.checkTransactionsAndConnect(stakeTreeFees, node, block.Transactions(), view, stxos, true) if err != nil { log.Tracef("checkTransactionsAndConnect failed for cur "+