dcrd/blockchain/subsidy.go
Donald Adu-Poku 48e43ca109 multi: Start dcrutil v4 module dev cycle.
This updates the dcrutil module version to v4
and updates import and deps sites.
2020-12-16 16:30:24 -06:00

194 lines
6.3 KiB
Go

// Copyright (c) 2013-2015 The btcsuite developers
// Copyright (c) 2015-2020 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"bytes"
"fmt"
"github.com/decred/dcrd/blockchain/stake/v4"
"github.com/decred/dcrd/blockchain/standalone/v2"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/decred/dcrd/dcrutil/v4"
"github.com/decred/dcrd/txscript/v4"
"github.com/decred/dcrd/wire"
)
// blockOneCoinbasePaysTokens checks to see if the first block coinbase pays
// out to the network initial token ledger.
func blockOneCoinbasePaysTokens(tx *dcrutil.Tx, params *chaincfg.Params) error {
// Nothing to do when there is no ledger specified.
if len(params.BlockOneLedger) == 0 {
return nil
}
if tx.MsgTx().LockTime != 0 {
str := "block 1 coinbase has invalid locktime"
return ruleError(ErrBlockOneTx, str)
}
if tx.MsgTx().Expiry != wire.NoExpiryValue {
str := "block 1 coinbase has invalid expiry"
return ruleError(ErrBlockOneTx, str)
}
if tx.MsgTx().TxIn[0].Sequence != wire.MaxTxInSequenceNum {
str := "block 1 coinbase not finalized"
return ruleError(ErrBlockOneInputs, str)
}
if len(tx.MsgTx().TxOut) == 0 {
str := "coinbase outputs empty in block 1"
return ruleError(ErrBlockOneOutputs, str)
}
ledger := params.BlockOneLedger
if len(ledger) != len(tx.MsgTx().TxOut) {
str := fmt.Sprintf("wrong number of outputs in block 1 coinbase; "+
"got %v, expected %v", len(tx.MsgTx().TxOut), len(ledger))
return ruleError(ErrBlockOneOutputs, str)
}
// Check the addresses and output amounts against those in the ledger.
const consensusScriptVersion = 0
for i, txOut := range tx.MsgTx().TxOut {
ledgerEntry := &ledger[i]
if txOut.Version != ledgerEntry.ScriptVersion {
str := fmt.Sprintf("block one output %d script version %d is not %d",
i, txOut.Version, consensusScriptVersion)
return ruleError(ErrBlockOneOutputs, str)
}
if !bytes.Equal(txOut.PkScript, ledgerEntry.Script) {
str := fmt.Sprintf("block one output %d script %x is not %x", i,
txOut.PkScript, ledgerEntry.Script)
return ruleError(ErrBlockOneOutputs, str)
}
if txOut.Value != ledgerEntry.Amount {
str := fmt.Sprintf("block one output %d generates %v instead of "+
"required %v", i, dcrutil.Amount(txOut.Value),
dcrutil.Amount(ledgerEntry.Amount))
return ruleError(ErrBlockOneOutputs, str)
}
}
return nil
}
// coinbasePaysTreasuryAddress checks to see if a given block's coinbase
// correctly pays the treasury prior to the agenda that modifies the treasury
// payout to happen via a treasurybase transaction in the stake tree instead.
func coinbasePaysTreasuryAddress(subsidyCache *standalone.SubsidyCache, tx *dcrutil.Tx, height int64, voters uint16, params *chaincfg.Params, isTreasuryEnabled bool) error {
// Treasury subsidy only applies from block 2 onwards.
if height <= 1 {
return nil
}
// Treasury subsidy is disabled.
if params.BlockTaxProportion == 0 {
return nil
}
if len(tx.MsgTx().TxOut) == 0 {
str := "invalid coinbase (no outputs)"
return ruleError(ErrNoTxOutputs, str)
}
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(treasuryOutput.PkScript, params.OrganizationPkScript) {
str := fmt.Sprintf("treasury output script is %x instead of %x",
treasuryOutput.PkScript, params.OrganizationPkScript)
return ruleError(ErrNoTax, str)
}
// 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,
isTreasuryEnabled)
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
}
// checkTreasuryBase checks to see if a given block's treasurybase correctly
// pays the treasury. This is the new function that uses the treasury base for
// the payout.
func checkTreasuryBase(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
}
// Treasury subsidy is disabled.
if params.BlockTaxProportion == 0 {
return nil
}
if len(tx.MsgTx().TxOut) != 2 {
// Can't get hit
return ruleError(ErrInvalidTreasurybaseTxOutputs,
fmt.Sprintf("invalid treasurybase number of outputs: %v",
len(tx.MsgTx().TxOut)))
}
treasuryOutput := tx.MsgTx().TxOut[0]
if treasuryOutput.Version != 0 {
// Can't get hit
str := fmt.Sprintf("treasury output version %d is instead of %d",
treasuryOutput.Version, 0)
return ruleError(ErrInvalidTreasurybaseVersion, str)
}
if len(treasuryOutput.PkScript) != 1 ||
treasuryOutput.PkScript[0] != txscript.OP_TADD {
// Can't get hit
str := fmt.Sprintf("treasury output script is %x instead of %x",
treasuryOutput.PkScript, params.OrganizationPkScript)
return ruleError(ErrInvalidTreasurybaseScript, str)
}
// Calculate the amount of subsidy that should have been paid out to the
// Treasury and ensure the subsidy generated is correct.
const withTreasury = true
orgSubsidy := subsidyCache.CalcTreasurySubsidy(height, voters,
withTreasury)
if orgSubsidy != treasuryOutput.Value {
str := fmt.Sprintf("treasury output amount is %s instead of %s",
dcrutil.Amount(treasuryOutput.Value), dcrutil.Amount(orgSubsidy))
return ruleError(ErrTreasurybaseOutValue, str)
}
return nil
}
// 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, isTreasuryEnabled bool) int64 {
var subsidy int64
if headerApprovesParent(&block.MsgBlock().Header) {
subsidy += parent.MsgBlock().Transactions[0].TxIn[0].ValueIn
}
for _, stx := range block.MsgBlock().STransactions {
if stake.IsSSGen(stx, isTreasuryEnabled) {
subsidy += stx.TxIn[0].ValueIn
}
}
return subsidy
}