mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-14 10:06:40 +00:00

* [NOD-540] Implement reachability (#545) * [NOD-540] Begin implementing reachability. * [NOD-540] Finish implementing reachability. * [NOD-540] Implement TestIsFutureBlock. * [NOD-540] Implement TestInsertFutureBlock. * [NOD-540] Add comments. * [NOD-540] Add comment for interval in blockNode. * [NOD-540] Updated comments over insertFutureBlock and isFutureBlock. * [NOD-540] Implement interval splitting methods. * [NOD-540] Begin implementing tree manipulation in blockNode. * [NOD-540] Implement countSubtreesUp. * [NOD-540] Add a comment explaining an impossible condition. * [NOD-540] Implement applyIntervalDown. * [NOD-540] Moved the reachability tree stuff into reachability.go. * [NOD-540] Add some comments. * [NOD-540] Add more comments, implement isInPast. * [NOD-540] Fix comments. * [NOD-540] Implement TestSplitFraction. * [NOD-540] Implement TestSplitExact. * [NOD-540] Implement TestSplit. * [NOD-540] Add comments to structs. * [NOD-540] Implement TestAddTreeChild. * [NOD-540] Fix a comment. * [NOD-540] Rename isInPast to isAncestorOf. * [NOD-540] Rename futureBlocks to futureCoveringSet. * [NOD-540] Rename isFutureBlock to isInFuture. * [NOD-540] move reachabilityInterval to the top of reachability.go. * [NOD-540] Change "s.t." to "such that" in a comment. * [NOD-540] Fix indentation. * [NOD-540] Fix a potential bug involving float inaccuracy. * [NOD-540] Wrote a more descriptive error message. * [NOD-540] Fix error messsage. * [NOD-540] Fix the recursive countSubtreesUp. * [NOD-540] Rename countSubtreesUp to countSubtrees and applyIntervalDown to propagateInterval. * [NOD-540] Implement updating reachability for a valid new block. * [NOD-540] Implement a disk storage for reachability data. * [NOD-540] Fix not all tree nodes being written to the database. * [NOD-540] Implement serialization for reachabilityData. * [NOD-540] Implement some deserialization for reachabilityData. * [NOD-540] Implement restoring the reachabilityStore on node restart. * [NOD-540] Made interval and remainingInterval pointers. * [NOD-540] Rename setTreeInterval to setInterval. * [NOD-540] Rename reindexTreeIntervals to reindexIntervals and fixed the comment above it. * [NOD-540] Expand the comment above reindexIntervals. * [NOD-540] Fix comment above countSubtrees. * [NOD-540] Fix comment above countSubtrees some more. * [NOD-540] Fix comment above split. * [NOD-540] Fix comment above isAncestorOf. * [NOD-540] Fix comment above reachabilityTreeNode. * [NOD-540] Fix weird condition in addTreeChild. * [NOD-540] Rename addTreeChild to addChild. * [NOD-540] Fix weird condition in splitFraction. * [NOD-540] Reverse the lines in reachabilityTreeNode.String(). * [NOD-540] Renamed f to fraction and x to size. * [NOD-540] Fix comment above bisect. * [NOD-540] Implement rtn.isAncestorOf(). * [NOD-540] Use treeNode isAncestorOf instead of treeInterval isAncestorOf. * [NOD-540] Use newReachabilityInterval instead of struct initialization. * [NOD-540] Make reachabilityTreeNode.String() use strings.Join. * [NOD-540] Use sync.RWMutex instead of locks.PriorityMutex. * [NOD-540] Rename thisTreeNode to newTreeNode. * [NOD-540] Rename setTreeNode to addTreeNode. * [NOD-540] Extracted selectedParentAnticone to a separate function. * [NOD-540] Rename node to this. * [NOD-540] Move updateReachability and isAncestorOf from dag.go to reachability.go. * [NOD-540] Add whitespace after multiline function signatures in reachability.go. * [NOD-540] Make splitFraction return an error on empty interval. * [NOD-540] Add a comment about rounding to splitFraction. * [NOD-540] Replace sneaky tabs with spaces. * [NOD-540] Rename split to splitExponential. * [NOD-540] Extract exponentialFractions to a separate function. * [NOD-540] Rename bisect to findIndex. * [NOD-540] Add call to reachabilityStore.clearDirtyEntries at the end of saveChangesFromBlock. * [NOD-540] Explain the dirty hack in reachabilityStore.init(). * [NOD-540] Split the function signature for deserializeReachabilityData to two lines. * [NOD-540] Add a comment about float precision loss to exponentialFractions. * [NOD-540] Corrected a comment about float precision loss to exponentialFractions. * [NOD-540] Fixed a comment about float precision loss to exponentialFractions some more. * [NOD-540] Added further comments above futureCoveringBlockSet. * [NOD-540] Rename addTreeNode to setTreeNode. * [NOD-540] Rename splitExponential to splitWithExponentialBias. * [NOD-540] Fix object references in reachabilityData deserialization (#563) * [NOD-540] Fix broken references in deserialization. * [NOD-540] Fix broken references in futureCoveringSet deserialization. Also add comments. * [NOD-540] Don't deserialize on the first pass in reachabilityStore.init(). * [NOD-540] Remove redundant assignment to loaded[hash]. * [NOD-540] Use NewHash instead of SetBytes. Rename data to destination. * [NOD-540] Preallocate futureCoveringSet. * [NOD-541] Implement GHOSTDAG (#560) * [NOD-541] Implement GHOSTDAG * [NOD-541] Replace the old PHANTOM variant with GHOSTDAG * [NOD-541] Move dag.updateReachability to the top of dag.applyDAGChanges to update reachability before the virtual block is updated * [NOD-541] Fix blueAnticoneSize * [NOD-541] Initialize node.bluesAnticoneSizes * [NOD-541] Fix pastUTXO and applyBlueBlocks blues order * [NOD-541] Add serialization logic to node.bluesAnticoneSizes * [NOD-541] Fix GHOSTDAG to not count the new block and the blue candidates anticone, add selected parent to blues, and save to node.bluesAnticoneSizes properly * [NOD-541] Fix test names in inner strings * [NOD-541] Writing TestGHOSTDAG * [NOD-541] In blueAnticoneSize change node->current * [NOD-541] name ghostdag return values * [NOD-541] fix ghostdag to return slice * [NOD-541] Split k-cluster violation rules * [NOD-541] Add missing space * [NOD-541] Add comment to ghostdag * [NOD-541] In selectedParentAnticone rename past->selectedParentPast * [NOD-541] Fix misrefernces to TestChainUpdates * [NOD-541] Fix ghostdag comment * [NOD-541] Make PrepareBlockForTest in blockdag package * [NOD-541] Make PrepareBlockForTest in blockdag package * [NOD-541] Assign to selectedParentAnticone[i] instead of appending * [NOD-541] Remove redundant forceTransactions arguments from PrepareBlockForTEST * [NOD-541] Add non-selected parents to anticoneHeap * [NOD-541] add test for ghostdag * [NOD-541] Add comments * [NOD-541] Use adjusted time for initializing blockNode * [NOD-541] Rename isAncestorOf -> isAncestorOfBlueCandidate * [NOD-541] Remove params from PrepareBlockForTest * [NOD-541] Fix TestChainHeight * [NOD-541] Remove recursive lock * [NOD-541] Fix TestTxIndexConnectBlock * [NOD-541] Fix TestBlueBlockWindow * [NOD-541] Put prepareAndProcessBlock in common_test.go * [NOD-541] Fix TestConfirmations * [NOD-541] Fix TestAcceptingBlock * [NOD-541] Fix TestDifficulty * [NOD-541] Fix TestVirtualBlock * [NOD-541] Fix TestSelectedPath * [NOD-541] Fix TestChainUpdates * [NOD-541] Shorten TestDifficulty test time * [NOD-541] Make PrepareBlockForTest use minimal valid block time * [NOD-541] Remove TODO comment * [NOD-541] Move blockdag related mining functions to mining.go * [NOD-541] Use NextBlockCoinbaseTransaction instead of NextBlockCoinbaseTransactionNoLock in NextCoinbaseFromAddress * [NOD-541] Remove useMinimalTime from BlockForMining * [NOD-541] Make MedianAdjustedTime a *BlockDAG method * [NOD-541] Fix ghostdag to use anticone slice instead of heap * [NOD-541] Fix NewBlockTemplate locks * [NOD-541] Fix ghostdag comments * [NOD-541] Convert MedianAdjustedTime to NextBlockTime * [NOD-541] Fix ghostdag comment * [NOD-541] Fix TestGHOSTDAG comment * [NOD-541] Add comment before sanity check * [NOD-541] Explicitly initialize .blues in ghostdag * [NOD-541] Rename *blockNode.lessThan to *blockNode.less * [NOD-541] Remove redundant check if block != chainBlock * [NOD-541] Fix comment * [NOD-541] Fix comment * [NOD-497] Add comment; General refactoring * [NOD-497] General refactoring. * [NOD-497] Use isAncestor of the tree rather than the node * [NOD-497] Remove reachability mutex lock as it is redundant (dag lock is held so no need); General refactoring. * [NOD-497] Update comment * [NOD-497] Undo test blocktimestamp * [NOD-497] Update comments; Use BlockNode.less for blockset; * [NOD-497] Change processBlock to return boolean and not the delay duration (merge conflict) * [NOD-497] Undo change for bluest to use less; Change blocknode less to use daghash.Less Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com> Co-authored-by: Dan Aharoni <dereeno@protonmail.com>
317 lines
12 KiB
Go
317 lines
12 KiB
Go
// Copyright (c) 2014-2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package mining
|
|
|
|
import (
|
|
"github.com/pkg/errors"
|
|
"time"
|
|
|
|
"github.com/kaspanet/kaspad/blockdag"
|
|
"github.com/kaspanet/kaspad/dagconfig"
|
|
"github.com/kaspanet/kaspad/txscript"
|
|
"github.com/kaspanet/kaspad/util"
|
|
"github.com/kaspanet/kaspad/util/daghash"
|
|
"github.com/kaspanet/kaspad/wire"
|
|
)
|
|
|
|
const (
|
|
// CoinbaseFlags is added to the coinbase script of a generated block
|
|
// and is used to monitor BIP16 support as well as blocks that are
|
|
// generated via kaspad.
|
|
CoinbaseFlags = "/kaspad/"
|
|
)
|
|
|
|
// TxDesc is a descriptor about a transaction in a transaction source along with
|
|
// additional metadata.
|
|
type TxDesc struct {
|
|
// Tx is the transaction associated with the entry.
|
|
Tx *util.Tx
|
|
|
|
// Added is the time when the entry was added to the source pool.
|
|
Added time.Time
|
|
|
|
// Height is the block height when the entry was added to the the source
|
|
// pool.
|
|
Height uint64
|
|
|
|
// Fee is the total fee the transaction associated with the entry pays.
|
|
Fee uint64
|
|
|
|
// FeePerKB is the fee the transaction pays in sompi per 1000 bytes.
|
|
FeePerKB uint64
|
|
}
|
|
|
|
// TxSource represents a source of transactions to consider for inclusion in
|
|
// new blocks.
|
|
//
|
|
// The interface contract requires that all of these methods are safe for
|
|
// concurrent access with respect to the source.
|
|
type TxSource interface {
|
|
// LastUpdated returns the last time a transaction was added to or
|
|
// removed from the source pool.
|
|
LastUpdated() time.Time
|
|
|
|
// MiningDescs returns a slice of mining descriptors for all the
|
|
// transactions in the source pool.
|
|
MiningDescs() []*TxDesc
|
|
|
|
// HaveTransaction returns whether or not the passed transaction hash
|
|
// exists in the source pool.
|
|
HaveTransaction(txID *daghash.TxID) bool
|
|
}
|
|
|
|
// BlockTemplate houses a block that has yet to be solved along with additional
|
|
// details about the fees and the number of signature operations for each
|
|
// transaction in the block.
|
|
type BlockTemplate struct {
|
|
// Block is a block that is ready to be solved by miners. Thus, it is
|
|
// completely valid with the exception of satisfying the proof-of-work
|
|
// requirement.
|
|
Block *wire.MsgBlock
|
|
|
|
// TxMasses contains the mass of each transaction in the generated
|
|
// template performs.
|
|
TxMasses []uint64
|
|
|
|
// Fees contains the amount of fees each transaction in the generated
|
|
// template pays in base units. Since the first transaction is the
|
|
// coinbase, the first entry (offset 0) will contain the negative of the
|
|
// sum of the fees of all other transactions.
|
|
Fees []uint64
|
|
|
|
// Height is the height at which the block template connects to the DAG
|
|
Height uint64
|
|
|
|
// ValidPayAddress indicates whether or not the template coinbase pays
|
|
// to an address or is redeemable by anyone. See the documentation on
|
|
// NewBlockTemplate for details on which this can be useful to generate
|
|
// templates without a coinbase payment address.
|
|
ValidPayAddress bool
|
|
}
|
|
|
|
// BlkTmplGenerator provides a type that can be used to generate block templates
|
|
// based on a given mining policy and source of transactions to choose from.
|
|
// It also houses additional state required in order to ensure the templates
|
|
// are built on top of the current DAG and adhere to the consensus rules.
|
|
type BlkTmplGenerator struct {
|
|
policy *Policy
|
|
dagParams *dagconfig.Params
|
|
txSource TxSource
|
|
dag *blockdag.BlockDAG
|
|
timeSource blockdag.MedianTimeSource
|
|
sigCache *txscript.SigCache
|
|
}
|
|
|
|
// NewBlkTmplGenerator returns a new block template generator for the given
|
|
// policy using transactions from the provided transaction source.
|
|
//
|
|
// The additional state-related fields are required in order to ensure the
|
|
// templates are built on top of the current DAG and adhere to the
|
|
// consensus rules.
|
|
func NewBlkTmplGenerator(policy *Policy, params *dagconfig.Params,
|
|
txSource TxSource, dag *blockdag.BlockDAG,
|
|
timeSource blockdag.MedianTimeSource,
|
|
sigCache *txscript.SigCache) *BlkTmplGenerator {
|
|
|
|
return &BlkTmplGenerator{
|
|
policy: policy,
|
|
dagParams: params,
|
|
txSource: txSource,
|
|
dag: dag,
|
|
timeSource: timeSource,
|
|
sigCache: sigCache,
|
|
}
|
|
}
|
|
|
|
// NewBlockTemplate 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 (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTemplate, error) {
|
|
g.dag.Lock()
|
|
defer g.dag.Unlock()
|
|
|
|
txsForBlockTemplate, err := g.selectTxs(payToAddress)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to select transactions: %s", err)
|
|
}
|
|
|
|
msgBlock, err := g.dag.BlockForMining(txsForBlockTemplate.selectedTxs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Finally, perform a full check on the created block against the DAG
|
|
// consensus rules to ensure it properly connects to the DAG with no
|
|
// issues.
|
|
block := util.NewBlock(msgBlock)
|
|
|
|
if err := g.dag.CheckConnectBlockTemplateNoLock(block); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.Debugf("Created new block template (%d transactions, %d in fees, "+
|
|
"%d mass, target difficulty %064x)",
|
|
len(msgBlock.Transactions), txsForBlockTemplate.totalFees,
|
|
txsForBlockTemplate.totalMass, util.CompactToBig(msgBlock.Header.Bits))
|
|
|
|
return &BlockTemplate{
|
|
Block: msgBlock,
|
|
TxMasses: txsForBlockTemplate.txMasses,
|
|
Fees: txsForBlockTemplate.txFees,
|
|
ValidPayAddress: payToAddress != nil,
|
|
}, nil
|
|
}
|
|
|
|
func (g *BlkTmplGenerator) buildUTXOCommitment(transactions []*wire.MsgTx) (*daghash.Hash, error) {
|
|
utxoWithTransactions, err := g.dag.UTXOSet().WithTransactions(transactions, blockdag.UnacceptedBlueScore, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return utxoWithTransactions.Multiset().Hash(), nil
|
|
}
|
|
|
|
// UpdateBlockTime updates the timestamp in the header of the passed block to
|
|
// the current time while taking into account the median time of the last
|
|
// several blocks to ensure the new time is after that time per the DAG
|
|
// consensus rules. Finally, it will update the target difficulty if needed
|
|
// based on the new time for the test networks since their target difficulty can
|
|
// change based upon time.
|
|
func (g *BlkTmplGenerator) UpdateBlockTime(msgBlock *wire.MsgBlock) error {
|
|
// The new timestamp is potentially adjusted to ensure it comes after
|
|
// the median time of the last several blocks per the DAG consensus
|
|
// rules.
|
|
msgBlock.Header.Timestamp = g.dag.NextBlockTime()
|
|
msgBlock.Header.Bits = g.dag.NextRequiredDifficulty(msgBlock.Header.Timestamp)
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateExtraNonce updates the extra nonce in the coinbase script of the passed
|
|
// block by regenerating the coinbase script with the passed value and block
|
|
// height. It also recalculates and updates the new merkle root that results
|
|
// from changing the coinbase script.
|
|
func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, extraNonce uint64) error {
|
|
coinbasePayloadScriptPubKey, _, err := blockdag.DeserializeCoinbasePayload(msgBlock.Transactions[util.CoinbaseTransactionIndex])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
coinbasePayloadExtraData, err := blockdag.CoinbasePayloadExtraData(extraNonce, CoinbaseFlags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
coinbasePayload, err := blockdag.SerializeCoinbasePayload(coinbasePayloadScriptPubKey, coinbasePayloadExtraData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(coinbasePayload) > blockdag.MaxCoinbasePayloadLen {
|
|
return errors.Errorf("coinbase transaction script length "+
|
|
"of %d is out of range (max: %d)",
|
|
len(coinbasePayload),
|
|
blockdag.MaxCoinbasePayloadLen)
|
|
}
|
|
oldCoinbaseTx := msgBlock.Transactions[util.CoinbaseTransactionIndex]
|
|
msgBlock.Transactions[util.CoinbaseTransactionIndex] = wire.NewSubnetworkMsgTx(oldCoinbaseTx.Version, oldCoinbaseTx.TxIn, oldCoinbaseTx.TxOut, &oldCoinbaseTx.SubnetworkID, oldCoinbaseTx.Gas, coinbasePayload)
|
|
|
|
// TODO(davec): A util.Block should use saved in the state to avoid
|
|
// recalculating all of the other transaction hashes.
|
|
// block.Transactions[util.CoinbaseTransactionIndex].InvalidateCache()
|
|
|
|
// Recalculate the merkle roots with the updated extra nonce.
|
|
block := util.NewBlock(msgBlock)
|
|
hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions())
|
|
msgBlock.Header.HashMerkleRoot = hashMerkleTree.Root()
|
|
|
|
// buildUTXOCommitment is the only function in UpdateExtraNonce that
|
|
// requires the dagLock, and as such we lock and unlock it locally.
|
|
utxoCommitment, err := func() (*daghash.Hash, error) {
|
|
g.dag.Lock()
|
|
defer g.dag.Unlock()
|
|
|
|
return g.buildUTXOCommitment(msgBlock.Transactions)
|
|
}()
|
|
|
|
msgBlock.Header.UTXOCommitment = utxoCommitment
|
|
|
|
return nil
|
|
}
|
|
|
|
// VirtualBlueScore returns the virtual block's current blue score
|
|
func (g *BlkTmplGenerator) VirtualBlueScore() uint64 {
|
|
return g.dag.VirtualBlueScore()
|
|
}
|
|
|
|
// TipHashes returns the hashes of the DAG's tips
|
|
func (g *BlkTmplGenerator) TipHashes() []*daghash.Hash {
|
|
return g.dag.TipHashes()
|
|
}
|
|
|
|
// TxSource returns the associated transaction source.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (g *BlkTmplGenerator) TxSource() TxSource {
|
|
return g.txSource
|
|
}
|