mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-23 23:36:56 +00:00

* Revert "[NOD-1500] Delete integration tests" This reverts commit fcb57a206690a884fa6afb69d5d493282954a8bf. * [NOD-1518] hashserialization -> consenusserialization * [NOD-1518] Fix add genesis to virtual * [NOD-1518] Fix a bug in SerializeCoinbasePayload. * [NOD-1518] Fix a loop error and make pastMedianTime behave correctly everywhere on genesis. * [NOD-1518] Fix another bug and an infinite loop. * [NOD-1518] Fix uninitialized slice. * [NOD-1518] Fix bad should-commit checks and another infinite loop. * [NOD-1518] Fix nil serialization. * [NOD-1518] Rename blockHash to currentBlockHash. * [NOD-1518] Move the check whether stagedVirtualUTXOSet != nil to the top of commitVirtualUTXODiff. * [NOD-1518] Simplify utxoDiffStore.Commit. * [NOD-1518] Unextract resolveBlockStatusAndCheckFinality. * [NOD-1518] Move no-transactions logic into CalculateIDMerkleRoot. * [NOD-1518] Remove redundant is-staged check. * [NOD-1518] Fix merge errors. * [NOD-1518] Don't write anything if utxoDiffChild is nil. * [NOD-1518] Stage virtualAcceptanceData and virtualMultiset. * [NOD-1518] Fix bugs in getBlockTemplate and submitBlock. * [NOD-1518] Fix bad validation order in validateHeaderInContext. * [NOD-1518] Fix bug in Next(). * [NOD-1518] Fix nil dereference of subnetworks in AddressCache. * [NOD-1518] Fix multisetStore.Get returning a pointer to a multiset that is changed in place. * [NOD-1518] Break on genesis in countSubtrees. * [NOD-1518] Fix createBlockLocator. * [NOD-1518] Fix MsgTxToDomainTransaction. * [NOD-1518] Set MaxTxVersion to 1. * [NOD-1518] Fix missing error handling, bug in MsgTxToDomainTransaction, and bad subnetwork equality check. * [NOD-1518] Fix bug in hasUTXOByOutpointFromStagedVirtualUTXODiff. * [NOD-1518] Remove irrelevant comments. * [NOD-1518] Generate transactions with sufficient fee in tx_relay_test. * [NOD-1518] Fix broken RPC handlers. * [NOD-1518] Fix merge errors. * [NOD-1518] Fix bad exists check in restorePastUTXO and missing genesis check in CalculatePastUTXOAndAcceptanceData. * [NOD-1518] Add a comment. * [NOD-1518] Use a regular mutex instead of a read-write mutex in consensus to avoid dealing with sneaky not-actually-read functions. * [NOD-1518] Fix a deadlock in GetVirtualSelectedParent. * [NOD-1518] Fix missing handler registration for CmdHeader. * [NOD-1518] Fix processHeader calling OnNewBlock and LogBlock. Also fix conversion errors in IBDRootUTXOSetAndBlock. * [NOD-1518] Fix bad Command() in MsgIBDRootUTXOSetAndBlock. * [NOD-1518] Fix bad SyncStateMissingUTXOSet logic in resolveSyncState. * [NOD-1518] Rename mode to syncState. * [NOD-1518] Fix headers-only blocks coming in after the consensus thinks it's synced. * [NOD-1518] Fix selectedChildIterator.Next not ignoring virtual, infinite loop in HashSet.Length(). * [NOD-1518] Fix not-properly wrapped IBD blocks. * [NOD-1518] Fix bad conversion in RequestIBDBlocks. * [NOD-1518] Fix bad string for CmdRequestHeaders. * [NOD-1518] Fix bad string for CmdDoneHeaders. * [NOD-1518] Fix bad Command() for MsgIBDRootNotFound. * [NOD-1518] Fix bad areHeaderTipsSyncedMaxTimeDifference value. * [NOD-1518] Add missing string for CmdRequestIBDBlocks. * [NOD-1518] Fix bad check for SyncStateMissingBlockBodies. * [NOD-1518] Fix bad timeout durations in tests. * [NOD-1518] Fix IBD blocks not calling OnNewBlock. * [NOD-1518] Change when IBD finishes. * [NOD-1518] Properly clone utxoDiffChild. * [NOD-1518] Fix merge errors. * [NOD-1518] Move call to LogBlock to into OnNewBlock. * [NOD-1518] Return "not implemented" in unimplemented RPC handlers. * [NOD-1518] Extract cloning of hashes to a method over DomainHash. * [NOD-1518] Use isHeaderOnlyBlock. * [NOD-1518] Use constants.TransactionVersion. * [NOD-1518] Break immediately if we reached the virtual in SelectedChildIterator. * [NOD-1518] Don't stage nil utxoDiffChild. * [NOD-1518] Properly check the genesis hash in CalculatePastUTXOAndAcceptanceData. * [NOD-1518] Explain why we break on current == nil in countSubtrees. * [NOD-1518] Add a comment explaining why we check against StatusValid in resolveSyncState. Co-authored-by: Mike Zak <feanorr@gmail.com> Co-authored-by: Ori Newman <orinewman1@gmail.com>
171 lines
7.1 KiB
Go
171 lines
7.1 KiB
Go
package blocktemplatebuilder
|
|
|
|
import (
|
|
"math"
|
|
"sort"
|
|
|
|
consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
|
miningmanagerapi "github.com/kaspanet/kaspad/domain/miningmanager/model"
|
|
"github.com/kaspanet/kaspad/util"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type candidateTx struct {
|
|
*consensusexternalapi.DomainTransaction
|
|
txValue float64
|
|
gasLimit uint64
|
|
|
|
p float64
|
|
start float64
|
|
end float64
|
|
|
|
isMarkedForDeletion bool
|
|
}
|
|
|
|
// blockTemplateBuilder creates block templates for a miner to consume
|
|
type blockTemplateBuilder struct {
|
|
consensus consensusexternalapi.Consensus
|
|
mempool miningmanagerapi.Mempool
|
|
policy policy
|
|
}
|
|
|
|
// New creates a new blockTemplateBuilder
|
|
func New(consensus consensusexternalapi.Consensus, mempool miningmanagerapi.Mempool, blockMaxMass uint64) miningmanagerapi.BlockTemplateBuilder {
|
|
return &blockTemplateBuilder{
|
|
consensus: consensus,
|
|
mempool: mempool,
|
|
policy: policy{BlockMaxMass: blockMaxMass},
|
|
}
|
|
}
|
|
|
|
// GetBlockTemplate creates a block template for a miner to consume
|
|
// GetBlockTemplate returns a new block template that is ready to be solved
|
|
// using the transactions from the passed transaction source pool and a coinbase
|
|
// that either pays to the passed address if it is not nil, or a coinbase that
|
|
// is redeemable by anyone if the passed address is nil. The nil address
|
|
// functionality is useful since there are cases such as the getblocktemplate
|
|
// RPC where external mining software is responsible for creating their own
|
|
// coinbase which will replace the one generated for the block template. Thus
|
|
// the need to have configured address can be avoided.
|
|
//
|
|
// The transactions selected and included are prioritized according to several
|
|
// factors. First, each transaction has a priority calculated based on its
|
|
// value, age of inputs, and size. Transactions which consist of larger
|
|
// amounts, older inputs, and small sizes have the highest priority. Second, a
|
|
// fee per kilobyte is calculated for each transaction. Transactions with a
|
|
// higher fee per kilobyte are preferred. Finally, the block generation related
|
|
// policy settings are all taken into account.
|
|
//
|
|
// Transactions which only spend outputs from other transactions already in the
|
|
// block DAG are immediately added to a priority queue which either
|
|
// prioritizes based on the priority (then fee per kilobyte) or the fee per
|
|
// kilobyte (then priority) depending on whether or not the BlockPrioritySize
|
|
// policy setting allots space for high-priority transactions. Transactions
|
|
// which spend outputs from other transactions in the source pool are added to a
|
|
// dependency map so they can be added to the priority queue once the
|
|
// transactions they depend on have been included.
|
|
//
|
|
// Once the high-priority area (if configured) has been filled with
|
|
// transactions, or the priority falls below what is considered high-priority,
|
|
// the priority queue is updated to prioritize by fees per kilobyte (then
|
|
// priority).
|
|
//
|
|
// When the fees per kilobyte drop below the TxMinFreeFee policy setting, the
|
|
// transaction will be skipped unless the BlockMinSize policy setting is
|
|
// nonzero, in which case the block will be filled with the low-fee/free
|
|
// transactions until the block size reaches that minimum size.
|
|
//
|
|
// Any transactions which would cause the block to exceed the BlockMaxMass
|
|
// policy setting, exceed the maximum allowed signature operations per block, or
|
|
// otherwise cause the block to be invalid are skipped.
|
|
//
|
|
// Given the above, a block generated by this function is of the following form:
|
|
//
|
|
// ----------------------------------- -- --
|
|
// | Coinbase Transaction | | |
|
|
// |-----------------------------------| | |
|
|
// | | | | ----- policy.BlockPrioritySize
|
|
// | High-priority Transactions | | |
|
|
// | | | |
|
|
// |-----------------------------------| | --
|
|
// | | |
|
|
// | | |
|
|
// | | |--- policy.BlockMaxMass
|
|
// | Transactions prioritized by fee | |
|
|
// | until <= policy.TxMinFreeFee | |
|
|
// | | |
|
|
// | | |
|
|
// | | |
|
|
// |-----------------------------------| |
|
|
// | Low-fee/Non high-priority (free) | |
|
|
// | transactions (while block size | |
|
|
// | <= policy.BlockMinSize) | |
|
|
// ----------------------------------- --
|
|
|
|
func (btb *blockTemplateBuilder) GetBlockTemplate(coinbaseData *consensusexternalapi.DomainCoinbaseData) (*consensusexternalapi.DomainBlock, error) {
|
|
mempoolTransactions := btb.mempool.Transactions()
|
|
candidateTxs := make([]*candidateTx, 0, len(mempoolTransactions))
|
|
for _, tx := range mempoolTransactions {
|
|
// Calculate the tx value
|
|
gasLimit := uint64(0)
|
|
if !subnetworks.IsBuiltInOrNative(tx.SubnetworkID) {
|
|
panic("We currently don't support non native subnetworks")
|
|
}
|
|
candidateTxs = append(candidateTxs, &candidateTx{
|
|
DomainTransaction: tx,
|
|
txValue: btb.calcTxValue(tx),
|
|
gasLimit: gasLimit,
|
|
})
|
|
}
|
|
|
|
// Sort the candidate txs by subnetworkID.
|
|
sort.Slice(candidateTxs, func(i, j int) bool {
|
|
return subnetworks.Less(candidateTxs[i].SubnetworkID, candidateTxs[j].SubnetworkID)
|
|
})
|
|
|
|
log.Debugf("Considering %d transactions for inclusion to new block",
|
|
len(candidateTxs))
|
|
|
|
blockTxs := btb.selectTransactions(candidateTxs)
|
|
blk, err := btb.consensus.BuildBlock(coinbaseData, blockTxs.selectedTxs)
|
|
|
|
invalidTxsErr := ruleerrors.ErrInvalidTransactionsInNewBlock{}
|
|
if errors.As(err, &invalidTxsErr) {
|
|
log.Criticalf("consensus.BuildBlock returned invalid txs in GetBlockTemplate: %s", err)
|
|
invalidTxs := make([]*consensusexternalapi.DomainTransaction, 0, len(invalidTxsErr.InvalidTransactions))
|
|
for _, tx := range invalidTxsErr.InvalidTransactions {
|
|
invalidTxs = append(invalidTxs, tx.Transaction)
|
|
}
|
|
btb.mempool.RemoveTransactions(invalidTxs)
|
|
// We can call this recursively without worry because this should almost never happen
|
|
return btb.GetBlockTemplate(coinbaseData)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.Debugf("Created new block template (%d transactions, %d in fees, %d mass, target difficulty %064x)",
|
|
len(blk.Transactions), blockTxs.totalFees, blockTxs.totalMass, util.CompactToBig(blk.Header.Bits))
|
|
|
|
return blk, nil
|
|
}
|
|
|
|
// calcTxValue calculates a value to be used in transaction selection.
|
|
// The higher the number the more likely it is that the transaction will be
|
|
// included in the block.
|
|
func (btb *blockTemplateBuilder) calcTxValue(tx *consensusexternalapi.DomainTransaction) float64 {
|
|
massLimit := btb.policy.BlockMaxMass
|
|
|
|
mass := tx.Mass
|
|
fee := tx.Fee
|
|
if subnetworks.IsBuiltInOrNative(tx.SubnetworkID) {
|
|
return float64(fee) / (float64(mass) / float64(massLimit))
|
|
}
|
|
// TODO: Replace with real gas once implemented
|
|
gasLimit := uint64(math.MaxUint64)
|
|
return float64(fee) / (float64(mass)/float64(massLimit) + float64(tx.Gas)/float64(gasLimit))
|
|
}
|