diff --git a/cpuminer.go b/cpuminer.go index 9e07fbd6..a45e70ee 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/mining" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -54,7 +55,7 @@ type cpuminerConfig struct { // BlockTemplateGenerator identifies the instance to use in order to // generate block templates that the miner will attempt to solve. - BlockTemplateGenerator *BlkTmplGenerator + BlockTemplateGenerator *mining.BlkTmplGenerator // MiningAddrs is a list of payment addresses to use for the generated // blocks. Each generated block will randomly choose one of them. @@ -90,7 +91,7 @@ type cpuminerConfig struct { // system which is typically sufficient. type CPUMiner struct { sync.Mutex - g *BlkTmplGenerator + g *mining.BlkTmplGenerator cfg cpuminerConfig numWorkers uint32 started bool diff --git a/log.go b/log.go index 35253d8c..440a66b9 100644 --- a/log.go +++ b/log.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcd/mempool" + "github.com/btcsuite/btcd/mining" "github.com/btcsuite/btcd/peer" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btclog" @@ -127,6 +128,7 @@ func useLogger(subsystemID string, logger btclog.Logger) { case "MINR": minrLog = logger + mining.UseLogger(logger) case "PEER": peerLog = logger diff --git a/mining.go b/mining.go deleted file mode 100644 index daeffcc6..00000000 --- a/mining.go +++ /dev/null @@ -1,837 +0,0 @@ -// 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 main - -import ( - "container/heap" - "fmt" - "time" - - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/mining" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" -) - -const ( - // generatedBlockVersion is the version of the block being generated. - // It is defined as a constant here rather than using the - // wire.BlockVersion constant since a change in the block version - // will require changes to the generated block. Using the wire constant - // for generated block version could allow creation of invalid blocks - // for the updated version. - generatedBlockVersion = 4 - - // blockHeaderOverhead is the max number of bytes it takes to serialize - // a block header and max possible transaction count. - blockHeaderOverhead = wire.MaxBlockHeaderPayload + wire.MaxVarIntPayload - - // 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 btcd. - coinbaseFlags = "/P2SH/btcd/" -) - -// txPrioItem houses a transaction along with extra information that allows the -// transaction to be prioritized and track dependencies on other transactions -// which have not been mined into a block yet. -type txPrioItem struct { - tx *btcutil.Tx - fee int64 - priority float64 - feePerKB int64 - - // dependsOn holds a map of transaction hashes which this one depends - // on. It will only be set when the transaction references other - // transactions in the source pool and hence must come after them in - // a block. - dependsOn map[chainhash.Hash]struct{} -} - -// txPriorityQueueLessFunc describes a function that can be used as a compare -// function for a transaction priority queue (txPriorityQueue). -type txPriorityQueueLessFunc func(*txPriorityQueue, int, int) bool - -// txPriorityQueue implements a priority queue of txPrioItem elements that -// supports an arbitrary compare function as defined by txPriorityQueueLessFunc. -type txPriorityQueue struct { - lessFunc txPriorityQueueLessFunc - items []*txPrioItem -} - -// Len returns the number of items in the priority queue. It is part of the -// heap.Interface implementation. -func (pq *txPriorityQueue) Len() int { - return len(pq.items) -} - -// Less returns whether the item in the priority queue with index i should sort -// before the item with index j by deferring to the assigned less function. It -// is part of the heap.Interface implementation. -func (pq *txPriorityQueue) Less(i, j int) bool { - return pq.lessFunc(pq, i, j) -} - -// Swap swaps the items at the passed indices in the priority queue. It is -// part of the heap.Interface implementation. -func (pq *txPriorityQueue) Swap(i, j int) { - pq.items[i], pq.items[j] = pq.items[j], pq.items[i] -} - -// Push pushes the passed item onto the priority queue. It is part of the -// heap.Interface implementation. -func (pq *txPriorityQueue) Push(x interface{}) { - pq.items = append(pq.items, x.(*txPrioItem)) -} - -// Pop removes the highest priority item (according to Less) from the priority -// queue and returns it. It is part of the heap.Interface implementation. -func (pq *txPriorityQueue) Pop() interface{} { - n := len(pq.items) - item := pq.items[n-1] - pq.items[n-1] = nil - pq.items = pq.items[0 : n-1] - return item -} - -// SetLessFunc sets the compare function for the priority queue to the provided -// function. It also invokes heap.Init on the priority queue using the new -// function so it can immediately be used with heap.Push/Pop. -func (pq *txPriorityQueue) SetLessFunc(lessFunc txPriorityQueueLessFunc) { - pq.lessFunc = lessFunc - heap.Init(pq) -} - -// txPQByPriority sorts a txPriorityQueue by transaction priority and then fees -// per kilobyte. -func txPQByPriority(pq *txPriorityQueue, i, j int) bool { - // Using > here so that pop gives the highest priority item as opposed - // to the lowest. Sort by priority first, then fee. - if pq.items[i].priority == pq.items[j].priority { - return pq.items[i].feePerKB > pq.items[j].feePerKB - } - return pq.items[i].priority > pq.items[j].priority - -} - -// txPQByFee sorts a txPriorityQueue by fees per kilobyte and then transaction -// priority. -func txPQByFee(pq *txPriorityQueue, i, j int) bool { - // Using > here so that pop gives the highest fee item as opposed - // to the lowest. Sort by fee first, then priority. - if pq.items[i].feePerKB == pq.items[j].feePerKB { - return pq.items[i].priority > pq.items[j].priority - } - return pq.items[i].feePerKB > pq.items[j].feePerKB -} - -// newTxPriorityQueue returns a new transaction priority queue that reserves the -// passed amount of space for the elements. The new priority queue uses either -// the txPQByPriority or the txPQByFee compare function depending on the -// sortByFee parameter and is already initialized for use with heap.Push/Pop. -// The priority queue can grow larger than the reserved space, but extra copies -// of the underlying array can be avoided by reserving a sane value. -func newTxPriorityQueue(reserve int, sortByFee bool) *txPriorityQueue { - pq := &txPriorityQueue{ - items: make([]*txPrioItem, 0, reserve), - } - if sortByFee { - pq.SetLessFunc(txPQByFee) - } else { - pq.SetLessFunc(txPQByPriority) - } - return pq -} - -// 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 - - // 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 []int64 - - // SigOpCounts contains the number of signature operations each - // transaction in the generated template performs. - SigOpCounts []int64 - - // Height is the height at which the block template connects to the main - // chain. - Height int32 - - // 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 -} - -// mergeUtxoView adds all of the entries in view to viewA. The result is that -// viewA will contain all of its original entries plus all of the entries -// in viewB. It will replace any entries in viewB which also exist in viewA -// if the entry in viewA is fully spent. -func mergeUtxoView(viewA *blockchain.UtxoViewpoint, viewB *blockchain.UtxoViewpoint) { - viewAEntries := viewA.Entries() - for hash, entryB := range viewB.Entries() { - if entryA, exists := viewAEntries[hash]; !exists || - entryA == nil || entryA.IsFullySpent() { - - viewAEntries[hash] = entryB - } - } -} - -// standardCoinbaseScript returns a standard script suitable for use as the -// signature script of the coinbase transaction of a new block. In particular, -// it starts with the block height that is required by version 2 blocks and adds -// the extra nonce as well as additional coinbase flags. -func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) { - return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)). - AddInt64(int64(extraNonce)).AddData([]byte(coinbaseFlags)). - Script() -} - -// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy -// based on the passed block height to the provided address. When the address -// is nil, the coinbase transaction will instead be redeemable by anyone. -// -// See the comment for NewBlockTemplate for more information about why the nil -// address handling is useful. -func createCoinbaseTx(params *chaincfg.Params, coinbaseScript []byte, nextBlockHeight int32, addr btcutil.Address) (*btcutil.Tx, error) { - // Create the script to pay to the provided payment address if one was - // specified. Otherwise create a script that allows the coinbase to be - // redeemable by anyone. - var pkScript []byte - if addr != nil { - var err error - pkScript, err = txscript.PayToAddrScript(addr) - if err != nil { - return nil, err - } - } else { - var err error - scriptBuilder := txscript.NewScriptBuilder() - pkScript, err = scriptBuilder.AddOp(txscript.OP_TRUE).Script() - if err != nil { - return nil, err - } - } - - tx := wire.NewMsgTx() - tx.AddTxIn(&wire.TxIn{ - // Coinbase transactions have no inputs, so previous outpoint is - // zero hash and max index. - PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{}, - wire.MaxPrevOutIndex), - SignatureScript: coinbaseScript, - Sequence: wire.MaxTxInSequenceNum, - }) - tx.AddTxOut(&wire.TxOut{ - Value: blockchain.CalcBlockSubsidy(nextBlockHeight, params), - PkScript: pkScript, - }) - return btcutil.NewTx(tx), nil -} - -// spendTransaction updates the passed view by marking the inputs to the passed -// transaction as spent. It also adds all outputs in the passed transaction -// which are not provably unspendable as available unspent transaction outputs. -func spendTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32) error { - for _, txIn := range tx.MsgTx().TxIn { - originHash := &txIn.PreviousOutPoint.Hash - originIndex := txIn.PreviousOutPoint.Index - entry := utxoView.LookupEntry(originHash) - if entry != nil { - entry.SpendOutput(originIndex) - } - } - - utxoView.AddTxOuts(tx, height) - return nil -} - -// logSkippedDeps logs any dependencies which are also skipped as a result of -// skipping a transaction while generating a block template at the trace level. -func logSkippedDeps(tx *btcutil.Tx, deps map[chainhash.Hash]*txPrioItem) { - if deps == nil { - return - } - - for _, item := range deps { - minrLog.Tracef("Skipping tx %s since it depends on %s\n", - item.tx.Hash(), tx.Hash()) - } -} - -// minimumMedianTime returns the minimum allowed timestamp for a block building -// on the end of the current best chain. In particular, it is one second after -// the median timestamp of the last several blocks per the chain consensus -// rules. -func minimumMedianTime(chainState *blockchain.BestState) time.Time { - return chainState.MedianTime.Add(time.Second) -} - -// medianAdjustedTime returns the current time adjusted to ensure it is at least -// one second after the median timestamp of the last several blocks per the -// chain consensus rules. -func medianAdjustedTime(chainState *blockchain.BestState, timeSource blockchain.MedianTimeSource) time.Time { - // The timestamp for the block must not be before the median timestamp - // of the last several blocks. Thus, choose the maximum between the - // current time and one second after the past median time. The current - // timestamp is truncated to a second boundary before comparison since a - // block timestamp does not supported a precision greater than one - // second. - newTimestamp := timeSource.AdjustedTime() - minTimestamp := minimumMedianTime(chainState) - if newTimestamp.Before(minTimestamp) { - newTimestamp = minTimestamp - } - - return newTimestamp -} - -// 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 best chain and adhere to the consensus rules. -// -// See the NewBlockTemplate method for a detailed description of how the block -// template is generated. -type BlkTmplGenerator struct { - policy *mining.Policy - chainParams *chaincfg.Params - txSource mining.TxSource - chain *blockchain.BlockChain - timeSource blockchain.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 best chain and adhere to the -// consensus rules. -func newBlkTmplGenerator(policy *mining.Policy, params *chaincfg.Params, - txSource mining.TxSource, chain *blockchain.BlockChain, - timeSource blockchain.MedianTimeSource, - sigCache *txscript.SigCache) *BlkTmplGenerator { - - return &BlkTmplGenerator{ - policy: policy, - chainParams: params, - txSource: txSource, - chain: chain, - 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 chain 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 BlockMaxSize -// 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.BlockMaxSize -// | Transactions prioritized by fee | | -// | until <= policy.TxMinFreeFee | | -// | | | -// | | | -// | | | -// |-----------------------------------| | -// | Low-fee/Non high-priority (free) | | -// | transactions (while block size | | -// | <= policy.BlockMinSize) | | -// ----------------------------------- -- -func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*BlockTemplate, error) { - // Extend the most recently known best block. - best := g.chain.BestSnapshot() - prevHash := best.Hash - nextBlockHeight := best.Height + 1 - - // Create a standard coinbase transaction paying to the provided - // address. NOTE: The coinbase value will be updated to include the - // fees from the selected transactions later after they have actually - // been selected. It is created here to detect any errors early - // before potentially doing a lot of work below. The extra nonce helps - // ensure the transaction is not a duplicate transaction (paying the - // same value to the same public key address would otherwise be an - // identical transaction for block version 1). - extraNonce := uint64(0) - coinbaseScript, err := standardCoinbaseScript(nextBlockHeight, extraNonce) - if err != nil { - return nil, err - } - coinbaseTx, err := createCoinbaseTx(g.chainParams, coinbaseScript, - nextBlockHeight, payToAddress) - if err != nil { - return nil, err - } - numCoinbaseSigOps := int64(blockchain.CountSigOps(coinbaseTx)) - - // Get the current source transactions and create a priority queue to - // hold the transactions which are ready for inclusion into a block - // along with some priority related and fee metadata. Reserve the same - // number of items that are available for the priority queue. Also, - // choose the initial sort order for the priority queue based on whether - // or not there is an area allocated for high-priority transactions. - sourceTxns := g.txSource.MiningDescs() - sortedByFee := g.policy.BlockPrioritySize == 0 - priorityQueue := newTxPriorityQueue(len(sourceTxns), sortedByFee) - - // Create a slice to hold the transactions to be included in the - // generated block with reserved space. Also create a utxo view to - // house all of the input transactions so multiple lookups can be - // avoided. - blockTxns := make([]*btcutil.Tx, 0, len(sourceTxns)) - blockTxns = append(blockTxns, coinbaseTx) - blockUtxos := blockchain.NewUtxoViewpoint() - - // dependers is used to track transactions which depend on another - // transaction in the source pool. This, in conjunction with the - // dependsOn map kept with each dependent transaction helps quickly - // determine which dependent transactions are now eligible for inclusion - // in the block once each transaction has been included. - dependers := make(map[chainhash.Hash]map[chainhash.Hash]*txPrioItem) - - // Create slices to hold the fees and number of signature operations - // for each of the selected transactions and add an entry for the - // coinbase. This allows the code below to simply append details about - // a transaction as it is selected for inclusion in the final block. - // However, since the total fees aren't known yet, use a dummy value for - // the coinbase fee which will be updated later. - txFees := make([]int64, 0, len(sourceTxns)) - txSigOpCounts := make([]int64, 0, len(sourceTxns)) - txFees = append(txFees, -1) // Updated once known - txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps) - - minrLog.Debugf("Considering %d transactions for inclusion to new block", - len(sourceTxns)) - -mempoolLoop: - for _, txDesc := range sourceTxns { - // A block can't have more than one coinbase or contain - // non-finalized transactions. - tx := txDesc.Tx - if blockchain.IsCoinBase(tx) { - minrLog.Tracef("Skipping coinbase tx %s", tx.Hash()) - continue - } - if !blockchain.IsFinalizedTransaction(tx, nextBlockHeight, - g.timeSource.AdjustedTime()) { - minrLog.Tracef("Skipping non-finalized tx %s", tx.Hash()) - continue - } - - // Fetch all of the utxos referenced by the this transaction. - // NOTE: This intentionally does not fetch inputs from the - // mempool since a transaction which depends on other - // transactions in the mempool must come after those - // dependencies in the final generated block. - utxos, err := g.chain.FetchUtxoView(tx) - if err != nil { - minrLog.Warnf("Unable to fetch utxo view for tx %s: "+ - "%v", tx.Hash(), err) - continue - } - - // Setup dependencies for any transactions which reference - // other transactions in the mempool so they can be properly - // ordered below. - prioItem := &txPrioItem{tx: tx} - for _, txIn := range tx.MsgTx().TxIn { - originHash := &txIn.PreviousOutPoint.Hash - originIndex := txIn.PreviousOutPoint.Index - utxoEntry := utxos.LookupEntry(originHash) - if utxoEntry == nil || utxoEntry.IsOutputSpent(originIndex) { - if !g.txSource.HaveTransaction(originHash) { - minrLog.Tracef("Skipping tx %s because "+ - "it references unspent output "+ - "%s which is not available", - tx.Hash(), txIn.PreviousOutPoint) - continue mempoolLoop - } - - // The transaction is referencing another - // transaction in the source pool, so setup an - // ordering dependency. - deps, exists := dependers[*originHash] - if !exists { - deps = make(map[chainhash.Hash]*txPrioItem) - dependers[*originHash] = deps - } - deps[*prioItem.tx.Hash()] = prioItem - if prioItem.dependsOn == nil { - prioItem.dependsOn = make( - map[chainhash.Hash]struct{}) - } - prioItem.dependsOn[*originHash] = struct{}{} - - // Skip the check below. We already know the - // referenced transaction is available. - continue - } - } - - // Calculate the final transaction priority using the input - // value age sum as well as the adjusted transaction size. The - // formula is: sum(inputValue * inputAge) / adjustedTxSize - prioItem.priority = mining.CalcPriority(tx.MsgTx(), utxos, - nextBlockHeight) - - // Calculate the fee in Satoshi/kB. - txSize := tx.MsgTx().SerializeSize() - prioItem.feePerKB = (txDesc.Fee * 1000) / int64(txSize) - prioItem.fee = txDesc.Fee - - // Add the transaction to the priority queue to mark it ready - // for inclusion in the block unless it has dependencies. - if prioItem.dependsOn == nil { - heap.Push(priorityQueue, prioItem) - } - - // Merge the referenced outputs from the input transactions to - // this transaction into the block utxo view. This allows the - // code below to avoid a second lookup. - mergeUtxoView(blockUtxos, utxos) - } - - minrLog.Tracef("Priority queue len %d, dependers len %d", - priorityQueue.Len(), len(dependers)) - - // The starting block size is the size of the block header plus the max - // possible transaction count size, plus the size of the coinbase - // transaction. - blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize()) - blockSigOps := numCoinbaseSigOps - totalFees := int64(0) - - // Choose which transactions make it into the block. - for priorityQueue.Len() > 0 { - // Grab the highest priority (or highest fee per kilobyte - // depending on the sort order) transaction. - prioItem := heap.Pop(priorityQueue).(*txPrioItem) - tx := prioItem.tx - - // Grab the list of transactions which depend on this one (if any). - deps := dependers[*tx.Hash()] - - // Enforce maximum block size. Also check for overflow. - txSize := uint32(tx.MsgTx().SerializeSize()) - blockPlusTxSize := blockSize + txSize - if blockPlusTxSize < blockSize || - blockPlusTxSize >= g.policy.BlockMaxSize { - - minrLog.Tracef("Skipping tx %s because it would exceed "+ - "the max block size", tx.Hash()) - logSkippedDeps(tx, deps) - continue - } - - // Enforce maximum signature operations per block. Also check - // for overflow. - numSigOps := int64(blockchain.CountSigOps(tx)) - if blockSigOps+numSigOps < blockSigOps || - blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { - minrLog.Tracef("Skipping tx %s because it would "+ - "exceed the maximum sigops per block", tx.Hash()) - logSkippedDeps(tx, deps) - continue - } - numP2SHSigOps, err := blockchain.CountP2SHSigOps(tx, false, - blockUtxos) - if err != nil { - minrLog.Tracef("Skipping tx %s due to error in "+ - "CountP2SHSigOps: %v", tx.Hash(), err) - logSkippedDeps(tx, deps) - continue - } - numSigOps += int64(numP2SHSigOps) - if blockSigOps+numSigOps < blockSigOps || - blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { - minrLog.Tracef("Skipping tx %s because it would "+ - "exceed the maximum sigops per block (p2sh)", - tx.Hash()) - logSkippedDeps(tx, deps) - continue - } - - // Skip free transactions once the block is larger than the - // minimum block size. - if sortedByFee && - prioItem.feePerKB < int64(g.policy.TxMinFreeFee) && - blockPlusTxSize >= g.policy.BlockMinSize { - - minrLog.Tracef("Skipping tx %s with feePerKB %d "+ - "< TxMinFreeFee %d and block size %d >= "+ - "minBlockSize %d", tx.Hash(), prioItem.feePerKB, - g.policy.TxMinFreeFee, blockPlusTxSize, - g.policy.BlockMinSize) - logSkippedDeps(tx, deps) - continue - } - - // Prioritize by fee per kilobyte once the block is larger than - // the priority size or there are no more high-priority - // transactions. - if !sortedByFee && (blockPlusTxSize >= g.policy.BlockPrioritySize || - prioItem.priority <= mining.MinHighPriority) { - - minrLog.Tracef("Switching to sort by fees per "+ - "kilobyte blockSize %d >= BlockPrioritySize "+ - "%d || priority %.2f <= minHighPriority %.2f", - blockPlusTxSize, g.policy.BlockPrioritySize, - prioItem.priority, mining.MinHighPriority) - - sortedByFee = true - priorityQueue.SetLessFunc(txPQByFee) - - // Put the transaction back into the priority queue and - // skip it so it is re-priortized by fees if it won't - // fit into the high-priority section or the priority is - // too low. Otherwise this transaction will be the - // final one in the high-priority section, so just fall - // though to the code below so it is added now. - if blockPlusTxSize > g.policy.BlockPrioritySize || - prioItem.priority < mining.MinHighPriority { - - heap.Push(priorityQueue, prioItem) - continue - } - } - - // Ensure the transaction inputs pass all of the necessary - // preconditions before allowing it to be added to the block. - _, err = blockchain.CheckTransactionInputs(tx, nextBlockHeight, - blockUtxos, g.chainParams) - if err != nil { - minrLog.Tracef("Skipping tx %s due to error in "+ - "CheckTransactionInputs: %v", tx.Hash(), err) - logSkippedDeps(tx, deps) - continue - } - err = blockchain.ValidateTransactionScripts(tx, blockUtxos, - txscript.StandardVerifyFlags, g.sigCache) - if err != nil { - minrLog.Tracef("Skipping tx %s due to error in "+ - "ValidateTransactionScripts: %v", tx.Hash(), err) - logSkippedDeps(tx, deps) - continue - } - - // Spend the transaction inputs in the block utxo view and add - // an entry for it to ensure any transactions which reference - // this one have it available as an input and can ensure they - // aren't double spending. - spendTransaction(blockUtxos, tx, nextBlockHeight) - - // Add the transaction to the block, increment counters, and - // save the fees and signature operation counts to the block - // template. - blockTxns = append(blockTxns, tx) - blockSize += txSize - blockSigOps += numSigOps - totalFees += prioItem.fee - txFees = append(txFees, prioItem.fee) - txSigOpCounts = append(txSigOpCounts, numSigOps) - - minrLog.Tracef("Adding tx %s (priority %.2f, feePerKB %d)", - prioItem.tx.Hash(), prioItem.priority, prioItem.feePerKB) - - // Add transactions which depend on this one (and also do not - // have any other unsatisified dependencies) to the priority - // queue. - for _, item := range deps { - // Add the transaction to the priority queue if there - // are no more dependencies after this one. - delete(item.dependsOn, *tx.Hash()) - if len(item.dependsOn) == 0 { - heap.Push(priorityQueue, item) - } - } - } - - // Now that the actual transactions have been selected, update the - // block size for the real transaction count and coinbase value with - // the total fees accordingly. - blockSize -= wire.MaxVarIntPayload - - uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) - coinbaseTx.MsgTx().TxOut[0].Value += totalFees - txFees[0] = -totalFees - - // Calculate the required difficulty for the block. The timestamp - // is potentially adjusted to ensure it comes after the median time of - // the last several blocks per the chain consensus rules. - ts := medianAdjustedTime(best, g.timeSource) - reqDifficulty, err := g.chain.CalcNextRequiredDifficulty(ts) - if err != nil { - return nil, err - } - - // Create a new block ready to be solved. - merkles := blockchain.BuildMerkleTreeStore(blockTxns) - var msgBlock wire.MsgBlock - msgBlock.Header = wire.BlockHeader{ - Version: generatedBlockVersion, - PrevBlock: *prevHash, - MerkleRoot: *merkles[len(merkles)-1], - Timestamp: ts, - Bits: reqDifficulty, - } - for _, tx := range blockTxns { - if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil { - return nil, err - } - } - - // Finally, perform a full check on the created block against the chain - // consensus rules to ensure it properly connects to the current best - // chain with no issues. - block := btcutil.NewBlock(&msgBlock) - block.SetHeight(nextBlockHeight) - if err := g.chain.CheckConnectBlock(block); err != nil { - return nil, err - } - - minrLog.Debugf("Created new block template (%d transactions, %d in "+ - "fees, %d signature operations, %d bytes, target difficulty "+ - "%064x)", len(msgBlock.Transactions), totalFees, blockSigOps, - blockSize, blockchain.CompactToBig(msgBlock.Header.Bits)) - - return &BlockTemplate{ - Block: &msgBlock, - Fees: txFees, - SigOpCounts: txSigOpCounts, - Height: nextBlockHeight, - ValidPayAddress: payToAddress != nil, - }, 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 chain -// 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 chain consensus - // rules. - newTime := medianAdjustedTime(g.chain.BestSnapshot(), g.timeSource) - msgBlock.Header.Timestamp = newTime - - // If running on a network that requires recalculating the difficulty, - // do so now. - if g.chainParams.ReduceMinDifficulty { - difficulty, err := g.chain.CalcNextRequiredDifficulty(newTime) - if err != nil { - return err - } - msgBlock.Header.Bits = difficulty - } - - 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, blockHeight int32, extraNonce uint64) error { - coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) - if err != nil { - return err - } - if len(coinbaseScript) > blockchain.MaxCoinbaseScriptLen { - return fmt.Errorf("coinbase transaction script length "+ - "of %d is out of range (min: %d, max: %d)", - len(coinbaseScript), blockchain.MinCoinbaseScriptLen, - blockchain.MaxCoinbaseScriptLen) - } - msgBlock.Transactions[0].TxIn[0].SignatureScript = coinbaseScript - - // TODO(davec): A btcutil.Block should use saved in the state to avoid - // recalculating all of the other transaction hashes. - // block.Transactions[0].InvalidateCache() - - // Recalculate the merkle root with the updated extra nonce. - block := btcutil.NewBlock(msgBlock) - merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) - msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] - return nil -} - -// BestSnapshot returns information about the current best chain block and -// related state as of the current point in time using the chain instance -// associated with the block template generator. The returned state must be -// treated as immutable since it is shared by all callers. -// -// This function is safe for concurrent access. -func (g *BlkTmplGenerator) BestSnapshot() *blockchain.BestState { - return g.chain.BestSnapshot() -} - -// TxSource returns the associated transaction source. -// -// This function is safe for concurrent access. -func (g *BlkTmplGenerator) TxSource() mining.TxSource { - return g.txSource -} diff --git a/mining/log.go b/mining/log.go new file mode 100644 index 00000000..09aa4024 --- /dev/null +++ b/mining/log.go @@ -0,0 +1,58 @@ +// Copyright (c) 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 ( + "errors" + "io" + + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} diff --git a/mining/mining.go b/mining/mining.go index 87c50598..d0763af7 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -5,9 +5,15 @@ package mining import ( + "container/heap" + "fmt" "time" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -15,6 +21,23 @@ const ( // MinHighPriority is the minimum priority value that allows a // transaction to be considered high priority. MinHighPriority = btcutil.SatoshiPerBitcoin * 144.0 / 250 + + // generatedBlockVersion is the version of the block being generated. + // It is defined as a constant here rather than using the + // wire.BlockVersion constant since a change in the block version + // will require changes to the generated block. Using the wire constant + // for generated block version could allow creation of invalid blocks + // for the updated version. + generatedBlockVersion = 4 + + // blockHeaderOverhead is the max number of bytes it takes to serialize + // a block header and max possible transaction count. + blockHeaderOverhead = wire.MaxBlockHeaderPayload + wire.MaxVarIntPayload + + // 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 btcd. + CoinbaseFlags = "/P2SH/btcd/" ) // TxDesc is a descriptor about a transaction in a transaction source along with @@ -52,3 +75,800 @@ type TxSource interface { // exists in the source pool. HaveTransaction(hash *chainhash.Hash) bool } + +// txPrioItem houses a transaction along with extra information that allows the +// transaction to be prioritized and track dependencies on other transactions +// which have not been mined into a block yet. +type txPrioItem struct { + tx *btcutil.Tx + fee int64 + priority float64 + feePerKB int64 + + // dependsOn holds a map of transaction hashes which this one depends + // on. It will only be set when the transaction references other + // transactions in the source pool and hence must come after them in + // a block. + dependsOn map[chainhash.Hash]struct{} +} + +// txPriorityQueueLessFunc describes a function that can be used as a compare +// function for a transaction priority queue (txPriorityQueue). +type txPriorityQueueLessFunc func(*txPriorityQueue, int, int) bool + +// txPriorityQueue implements a priority queue of txPrioItem elements that +// supports an arbitrary compare function as defined by txPriorityQueueLessFunc. +type txPriorityQueue struct { + lessFunc txPriorityQueueLessFunc + items []*txPrioItem +} + +// Len returns the number of items in the priority queue. It is part of the +// heap.Interface implementation. +func (pq *txPriorityQueue) Len() int { + return len(pq.items) +} + +// Less returns whether the item in the priority queue with index i should sort +// before the item with index j by deferring to the assigned less function. It +// is part of the heap.Interface implementation. +func (pq *txPriorityQueue) Less(i, j int) bool { + return pq.lessFunc(pq, i, j) +} + +// Swap swaps the items at the passed indices in the priority queue. It is +// part of the heap.Interface implementation. +func (pq *txPriorityQueue) Swap(i, j int) { + pq.items[i], pq.items[j] = pq.items[j], pq.items[i] +} + +// Push pushes the passed item onto the priority queue. It is part of the +// heap.Interface implementation. +func (pq *txPriorityQueue) Push(x interface{}) { + pq.items = append(pq.items, x.(*txPrioItem)) +} + +// Pop removes the highest priority item (according to Less) from the priority +// queue and returns it. It is part of the heap.Interface implementation. +func (pq *txPriorityQueue) Pop() interface{} { + n := len(pq.items) + item := pq.items[n-1] + pq.items[n-1] = nil + pq.items = pq.items[0 : n-1] + return item +} + +// SetLessFunc sets the compare function for the priority queue to the provided +// function. It also invokes heap.Init on the priority queue using the new +// function so it can immediately be used with heap.Push/Pop. +func (pq *txPriorityQueue) SetLessFunc(lessFunc txPriorityQueueLessFunc) { + pq.lessFunc = lessFunc + heap.Init(pq) +} + +// txPQByPriority sorts a txPriorityQueue by transaction priority and then fees +// per kilobyte. +func txPQByPriority(pq *txPriorityQueue, i, j int) bool { + // Using > here so that pop gives the highest priority item as opposed + // to the lowest. Sort by priority first, then fee. + if pq.items[i].priority == pq.items[j].priority { + return pq.items[i].feePerKB > pq.items[j].feePerKB + } + return pq.items[i].priority > pq.items[j].priority + +} + +// txPQByFee sorts a txPriorityQueue by fees per kilobyte and then transaction +// priority. +func txPQByFee(pq *txPriorityQueue, i, j int) bool { + // Using > here so that pop gives the highest fee item as opposed + // to the lowest. Sort by fee first, then priority. + if pq.items[i].feePerKB == pq.items[j].feePerKB { + return pq.items[i].priority > pq.items[j].priority + } + return pq.items[i].feePerKB > pq.items[j].feePerKB +} + +// newTxPriorityQueue returns a new transaction priority queue that reserves the +// passed amount of space for the elements. The new priority queue uses either +// the txPQByPriority or the txPQByFee compare function depending on the +// sortByFee parameter and is already initialized for use with heap.Push/Pop. +// The priority queue can grow larger than the reserved space, but extra copies +// of the underlying array can be avoided by reserving a sane value. +func newTxPriorityQueue(reserve int, sortByFee bool) *txPriorityQueue { + pq := &txPriorityQueue{ + items: make([]*txPrioItem, 0, reserve), + } + if sortByFee { + pq.SetLessFunc(txPQByFee) + } else { + pq.SetLessFunc(txPQByPriority) + } + return pq +} + +// 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 + + // 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 []int64 + + // SigOpCounts contains the number of signature operations each + // transaction in the generated template performs. + SigOpCounts []int64 + + // Height is the height at which the block template connects to the main + // chain. + Height int32 + + // 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 +} + +// mergeUtxoView adds all of the entries in view to viewA. The result is that +// viewA will contain all of its original entries plus all of the entries +// in viewB. It will replace any entries in viewB which also exist in viewA +// if the entry in viewA is fully spent. +func mergeUtxoView(viewA *blockchain.UtxoViewpoint, viewB *blockchain.UtxoViewpoint) { + viewAEntries := viewA.Entries() + for hash, entryB := range viewB.Entries() { + if entryA, exists := viewAEntries[hash]; !exists || + entryA == nil || entryA.IsFullySpent() { + + viewAEntries[hash] = entryB + } + } +} + +// standardCoinbaseScript returns a standard script suitable for use as the +// signature script of the coinbase transaction of a new block. In particular, +// it starts with the block height that is required by version 2 blocks and adds +// the extra nonce as well as additional coinbase flags. +func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) { + return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)). + AddInt64(int64(extraNonce)).AddData([]byte(CoinbaseFlags)). + Script() +} + +// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy +// based on the passed block height to the provided address. When the address +// is nil, the coinbase transaction will instead be redeemable by anyone. +// +// See the comment for NewBlockTemplate for more information about why the nil +// address handling is useful. +func createCoinbaseTx(params *chaincfg.Params, coinbaseScript []byte, nextBlockHeight int32, addr btcutil.Address) (*btcutil.Tx, error) { + // Create the script to pay to the provided payment address if one was + // specified. Otherwise create a script that allows the coinbase to be + // redeemable by anyone. + var pkScript []byte + if addr != nil { + var err error + pkScript, err = txscript.PayToAddrScript(addr) + if err != nil { + return nil, err + } + } else { + var err error + scriptBuilder := txscript.NewScriptBuilder() + pkScript, err = scriptBuilder.AddOp(txscript.OP_TRUE).Script() + if err != nil { + return nil, err + } + } + + tx := wire.NewMsgTx() + tx.AddTxIn(&wire.TxIn{ + // Coinbase transactions have no inputs, so previous outpoint is + // zero hash and max index. + PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{}, + wire.MaxPrevOutIndex), + SignatureScript: coinbaseScript, + Sequence: wire.MaxTxInSequenceNum, + }) + tx.AddTxOut(&wire.TxOut{ + Value: blockchain.CalcBlockSubsidy(nextBlockHeight, params), + PkScript: pkScript, + }) + return btcutil.NewTx(tx), nil +} + +// spendTransaction updates the passed view by marking the inputs to the passed +// transaction as spent. It also adds all outputs in the passed transaction +// which are not provably unspendable as available unspent transaction outputs. +func spendTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32) error { + for _, txIn := range tx.MsgTx().TxIn { + originHash := &txIn.PreviousOutPoint.Hash + originIndex := txIn.PreviousOutPoint.Index + entry := utxoView.LookupEntry(originHash) + if entry != nil { + entry.SpendOutput(originIndex) + } + } + + utxoView.AddTxOuts(tx, height) + return nil +} + +// logSkippedDeps logs any dependencies which are also skipped as a result of +// skipping a transaction while generating a block template at the trace level. +func logSkippedDeps(tx *btcutil.Tx, deps map[chainhash.Hash]*txPrioItem) { + if deps == nil { + return + } + + for _, item := range deps { + log.Tracef("Skipping tx %s since it depends on %s\n", + item.tx.Hash(), tx.Hash()) + } +} + +// MinimumMedianTime returns the minimum allowed timestamp for a block building +// on the end of the provided best chain. In particular, it is one second after +// the median timestamp of the last several blocks per the chain consensus +// rules. +func MinimumMedianTime(chainState *blockchain.BestState) time.Time { + return chainState.MedianTime.Add(time.Second) +} + +// medianAdjustedTime returns the current time adjusted to ensure it is at least +// one second after the median timestamp of the last several blocks per the +// chain consensus rules. +func medianAdjustedTime(chainState *blockchain.BestState, timeSource blockchain.MedianTimeSource) time.Time { + // The timestamp for the block must not be before the median timestamp + // of the last several blocks. Thus, choose the maximum between the + // current time and one second after the past median time. The current + // timestamp is truncated to a second boundary before comparison since a + // block timestamp does not supported a precision greater than one + // second. + newTimestamp := timeSource.AdjustedTime() + minTimestamp := MinimumMedianTime(chainState) + if newTimestamp.Before(minTimestamp) { + newTimestamp = minTimestamp + } + + return newTimestamp +} + +// 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 best chain and adhere to the consensus rules. +type BlkTmplGenerator struct { + policy *Policy + chainParams *chaincfg.Params + txSource TxSource + chain *blockchain.BlockChain + timeSource blockchain.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 best chain and adhere to the +// consensus rules. +func NewBlkTmplGenerator(policy *Policy, params *chaincfg.Params, + txSource TxSource, chain *blockchain.BlockChain, + timeSource blockchain.MedianTimeSource, + sigCache *txscript.SigCache) *BlkTmplGenerator { + + return &BlkTmplGenerator{ + policy: policy, + chainParams: params, + txSource: txSource, + chain: chain, + 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 chain 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 BlockMaxSize +// 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.BlockMaxSize +// | Transactions prioritized by fee | | +// | until <= policy.TxMinFreeFee | | +// | | | +// | | | +// | | | +// |-----------------------------------| | +// | Low-fee/Non high-priority (free) | | +// | transactions (while block size | | +// | <= policy.BlockMinSize) | | +// ----------------------------------- -- +func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*BlockTemplate, error) { + // Extend the most recently known best block. + best := g.chain.BestSnapshot() + prevHash := best.Hash + nextBlockHeight := best.Height + 1 + + // Create a standard coinbase transaction paying to the provided + // address. NOTE: The coinbase value will be updated to include the + // fees from the selected transactions later after they have actually + // been selected. It is created here to detect any errors early + // before potentially doing a lot of work below. The extra nonce helps + // ensure the transaction is not a duplicate transaction (paying the + // same value to the same public key address would otherwise be an + // identical transaction for block version 1). + extraNonce := uint64(0) + coinbaseScript, err := standardCoinbaseScript(nextBlockHeight, extraNonce) + if err != nil { + return nil, err + } + coinbaseTx, err := createCoinbaseTx(g.chainParams, coinbaseScript, + nextBlockHeight, payToAddress) + if err != nil { + return nil, err + } + numCoinbaseSigOps := int64(blockchain.CountSigOps(coinbaseTx)) + + // Get the current source transactions and create a priority queue to + // hold the transactions which are ready for inclusion into a block + // along with some priority related and fee metadata. Reserve the same + // number of items that are available for the priority queue. Also, + // choose the initial sort order for the priority queue based on whether + // or not there is an area allocated for high-priority transactions. + sourceTxns := g.txSource.MiningDescs() + sortedByFee := g.policy.BlockPrioritySize == 0 + priorityQueue := newTxPriorityQueue(len(sourceTxns), sortedByFee) + + // Create a slice to hold the transactions to be included in the + // generated block with reserved space. Also create a utxo view to + // house all of the input transactions so multiple lookups can be + // avoided. + blockTxns := make([]*btcutil.Tx, 0, len(sourceTxns)) + blockTxns = append(blockTxns, coinbaseTx) + blockUtxos := blockchain.NewUtxoViewpoint() + + // dependers is used to track transactions which depend on another + // transaction in the source pool. This, in conjunction with the + // dependsOn map kept with each dependent transaction helps quickly + // determine which dependent transactions are now eligible for inclusion + // in the block once each transaction has been included. + dependers := make(map[chainhash.Hash]map[chainhash.Hash]*txPrioItem) + + // Create slices to hold the fees and number of signature operations + // for each of the selected transactions and add an entry for the + // coinbase. This allows the code below to simply append details about + // a transaction as it is selected for inclusion in the final block. + // However, since the total fees aren't known yet, use a dummy value for + // the coinbase fee which will be updated later. + txFees := make([]int64, 0, len(sourceTxns)) + txSigOpCounts := make([]int64, 0, len(sourceTxns)) + txFees = append(txFees, -1) // Updated once known + txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps) + + log.Debugf("Considering %d transactions for inclusion to new block", + len(sourceTxns)) + +mempoolLoop: + for _, txDesc := range sourceTxns { + // A block can't have more than one coinbase or contain + // non-finalized transactions. + tx := txDesc.Tx + if blockchain.IsCoinBase(tx) { + log.Tracef("Skipping coinbase tx %s", tx.Hash()) + continue + } + if !blockchain.IsFinalizedTransaction(tx, nextBlockHeight, + g.timeSource.AdjustedTime()) { + + log.Tracef("Skipping non-finalized tx %s", tx.Hash()) + continue + } + + // Fetch all of the utxos referenced by the this transaction. + // NOTE: This intentionally does not fetch inputs from the + // mempool since a transaction which depends on other + // transactions in the mempool must come after those + // dependencies in the final generated block. + utxos, err := g.chain.FetchUtxoView(tx) + if err != nil { + log.Warnf("Unable to fetch utxo view for tx %s: %v", + tx.Hash(), err) + continue + } + + // Setup dependencies for any transactions which reference + // other transactions in the mempool so they can be properly + // ordered below. + prioItem := &txPrioItem{tx: tx} + for _, txIn := range tx.MsgTx().TxIn { + originHash := &txIn.PreviousOutPoint.Hash + originIndex := txIn.PreviousOutPoint.Index + utxoEntry := utxos.LookupEntry(originHash) + if utxoEntry == nil || utxoEntry.IsOutputSpent(originIndex) { + if !g.txSource.HaveTransaction(originHash) { + log.Tracef("Skipping tx %s because it "+ + "references unspent output %s "+ + "which is not available", + tx.Hash(), txIn.PreviousOutPoint) + continue mempoolLoop + } + + // The transaction is referencing another + // transaction in the source pool, so setup an + // ordering dependency. + deps, exists := dependers[*originHash] + if !exists { + deps = make(map[chainhash.Hash]*txPrioItem) + dependers[*originHash] = deps + } + deps[*prioItem.tx.Hash()] = prioItem + if prioItem.dependsOn == nil { + prioItem.dependsOn = make( + map[chainhash.Hash]struct{}) + } + prioItem.dependsOn[*originHash] = struct{}{} + + // Skip the check below. We already know the + // referenced transaction is available. + continue + } + } + + // Calculate the final transaction priority using the input + // value age sum as well as the adjusted transaction size. The + // formula is: sum(inputValue * inputAge) / adjustedTxSize + prioItem.priority = CalcPriority(tx.MsgTx(), utxos, + nextBlockHeight) + + // Calculate the fee in Satoshi/kB. + txSize := tx.MsgTx().SerializeSize() + prioItem.feePerKB = (txDesc.Fee * 1000) / int64(txSize) + prioItem.fee = txDesc.Fee + + // Add the transaction to the priority queue to mark it ready + // for inclusion in the block unless it has dependencies. + if prioItem.dependsOn == nil { + heap.Push(priorityQueue, prioItem) + } + + // Merge the referenced outputs from the input transactions to + // this transaction into the block utxo view. This allows the + // code below to avoid a second lookup. + mergeUtxoView(blockUtxos, utxos) + } + + log.Tracef("Priority queue len %d, dependers len %d", + priorityQueue.Len(), len(dependers)) + + // The starting block size is the size of the block header plus the max + // possible transaction count size, plus the size of the coinbase + // transaction. + blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize()) + blockSigOps := numCoinbaseSigOps + totalFees := int64(0) + + // Choose which transactions make it into the block. + for priorityQueue.Len() > 0 { + // Grab the highest priority (or highest fee per kilobyte + // depending on the sort order) transaction. + prioItem := heap.Pop(priorityQueue).(*txPrioItem) + tx := prioItem.tx + + // Grab any transactions which depend on this one. + deps := dependers[*tx.Hash()] + + // Enforce maximum block size. Also check for overflow. + txSize := uint32(tx.MsgTx().SerializeSize()) + blockPlusTxSize := blockSize + txSize + if blockPlusTxSize < blockSize || + blockPlusTxSize >= g.policy.BlockMaxSize { + + log.Tracef("Skipping tx %s because it would exceed "+ + "the max block size", tx.Hash()) + logSkippedDeps(tx, deps) + continue + } + + // Enforce maximum signature operations per block. Also check + // for overflow. + numSigOps := int64(blockchain.CountSigOps(tx)) + if blockSigOps+numSigOps < blockSigOps || + blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { + log.Tracef("Skipping tx %s because it would exceed "+ + "the maximum sigops per block", tx.Hash()) + logSkippedDeps(tx, deps) + continue + } + numP2SHSigOps, err := blockchain.CountP2SHSigOps(tx, false, + blockUtxos) + if err != nil { + log.Tracef("Skipping tx %s due to error in "+ + "CountP2SHSigOps: %v", tx.Hash(), err) + logSkippedDeps(tx, deps) + continue + } + numSigOps += int64(numP2SHSigOps) + if blockSigOps+numSigOps < blockSigOps || + blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { + log.Tracef("Skipping tx %s because it would exceed "+ + "the maximum sigops per block (p2sh)", + tx.Hash()) + logSkippedDeps(tx, deps) + continue + } + + // Skip free transactions once the block is larger than the + // minimum block size. + if sortedByFee && + prioItem.feePerKB < int64(g.policy.TxMinFreeFee) && + blockPlusTxSize >= g.policy.BlockMinSize { + + log.Tracef("Skipping tx %s with feePerKB %.2f "+ + "< TxMinFreeFee %d and block size %d >= "+ + "minBlockSize %d", tx.Hash(), prioItem.feePerKB, + g.policy.TxMinFreeFee, blockPlusTxSize, + g.policy.BlockMinSize) + logSkippedDeps(tx, deps) + continue + } + + // Prioritize by fee per kilobyte once the block is larger than + // the priority size or there are no more high-priority + // transactions. + if !sortedByFee && (blockPlusTxSize >= g.policy.BlockPrioritySize || + prioItem.priority <= MinHighPriority) { + + log.Tracef("Switching to sort by fees per kilobyte "+ + "blockSize %d >= BlockPrioritySize %d || "+ + "priority %.2f <= minHighPriority %.2f", + blockPlusTxSize, g.policy.BlockPrioritySize, + prioItem.priority, MinHighPriority) + + sortedByFee = true + priorityQueue.SetLessFunc(txPQByFee) + + // Put the transaction back into the priority queue and + // skip it so it is re-priortized by fees if it won't + // fit into the high-priority section or the priority is + // too low. Otherwise this transaction will be the + // final one in the high-priority section, so just fall + // though to the code below so it is added now. + if blockPlusTxSize > g.policy.BlockPrioritySize || + prioItem.priority < MinHighPriority { + + heap.Push(priorityQueue, prioItem) + continue + } + } + + // Ensure the transaction inputs pass all of the necessary + // preconditions before allowing it to be added to the block. + _, err = blockchain.CheckTransactionInputs(tx, nextBlockHeight, + blockUtxos, g.chainParams) + if err != nil { + log.Tracef("Skipping tx %s due to error in "+ + "CheckTransactionInputs: %v", tx.Hash(), err) + logSkippedDeps(tx, deps) + continue + } + err = blockchain.ValidateTransactionScripts(tx, blockUtxos, + txscript.StandardVerifyFlags, g.sigCache) + if err != nil { + log.Tracef("Skipping tx %s due to error in "+ + "ValidateTransactionScripts: %v", tx.Hash(), + err) + logSkippedDeps(tx, deps) + continue + } + + // Spend the transaction inputs in the block utxo view and add + // an entry for it to ensure any transactions which reference + // this one have it available as an input and can ensure they + // aren't double spending. + spendTransaction(blockUtxos, tx, nextBlockHeight) + + // Add the transaction to the block, increment counters, and + // save the fees and signature operation counts to the block + // template. + blockTxns = append(blockTxns, tx) + blockSize += txSize + blockSigOps += numSigOps + totalFees += prioItem.fee + txFees = append(txFees, prioItem.fee) + txSigOpCounts = append(txSigOpCounts, numSigOps) + + log.Tracef("Adding tx %s (priority %.2f, feePerKB %.2f)", + prioItem.tx.Hash(), prioItem.priority, prioItem.feePerKB) + + // Add transactions which depend on this one (and also do not + // have any other unsatisified dependencies) to the priority + // queue. + for _, item := range deps { + // Add the transaction to the priority queue if there + // are no more dependencies after this one. + delete(item.dependsOn, *tx.Hash()) + if len(item.dependsOn) == 0 { + heap.Push(priorityQueue, item) + } + } + } + + // Now that the actual transactions have been selected, update the + // block size for the real transaction count and coinbase value with + // the total fees accordingly. + blockSize -= wire.MaxVarIntPayload - + uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) + coinbaseTx.MsgTx().TxOut[0].Value += totalFees + txFees[0] = -totalFees + + // Calculate the required difficulty for the block. The timestamp + // is potentially adjusted to ensure it comes after the median time of + // the last several blocks per the chain consensus rules. + ts := medianAdjustedTime(best, g.timeSource) + reqDifficulty, err := g.chain.CalcNextRequiredDifficulty(ts) + if err != nil { + return nil, err + } + + // Create a new block ready to be solved. + merkles := blockchain.BuildMerkleTreeStore(blockTxns) + var msgBlock wire.MsgBlock + msgBlock.Header = wire.BlockHeader{ + Version: generatedBlockVersion, + PrevBlock: *prevHash, + MerkleRoot: *merkles[len(merkles)-1], + Timestamp: ts, + Bits: reqDifficulty, + } + for _, tx := range blockTxns { + if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil { + return nil, err + } + } + + // Finally, perform a full check on the created block against the chain + // consensus rules to ensure it properly connects to the current best + // chain with no issues. + block := btcutil.NewBlock(&msgBlock) + block.SetHeight(nextBlockHeight) + if err := g.chain.CheckConnectBlock(block); err != nil { + return nil, err + } + + log.Debugf("Created new block template (%d transactions, %d in fees, "+ + "%d signature operations, %d bytes, target difficulty %064x)", + len(msgBlock.Transactions), totalFees, blockSigOps, blockSize, + blockchain.CompactToBig(msgBlock.Header.Bits)) + + return &BlockTemplate{ + Block: &msgBlock, + Fees: txFees, + SigOpCounts: txSigOpCounts, + Height: nextBlockHeight, + ValidPayAddress: payToAddress != nil, + }, 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 chain +// 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 chain consensus + // rules. + newTime := medianAdjustedTime(g.chain.BestSnapshot(), g.timeSource) + msgBlock.Header.Timestamp = newTime + + // Recalculate the difficulty if running on a network that requires it. + if g.chainParams.ReduceMinDifficulty { + difficulty, err := g.chain.CalcNextRequiredDifficulty(newTime) + if err != nil { + return err + } + msgBlock.Header.Bits = difficulty + } + + 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, blockHeight int32, extraNonce uint64) error { + coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) + if err != nil { + return err + } + if len(coinbaseScript) > blockchain.MaxCoinbaseScriptLen { + return fmt.Errorf("coinbase transaction script length "+ + "of %d is out of range (min: %d, max: %d)", + len(coinbaseScript), blockchain.MinCoinbaseScriptLen, + blockchain.MaxCoinbaseScriptLen) + } + msgBlock.Transactions[0].TxIn[0].SignatureScript = coinbaseScript + + // TODO(davec): A btcutil.Block should use saved in the state to avoid + // recalculating all of the other transaction hashes. + // block.Transactions[0].InvalidateCache() + + // Recalculate the merkle root with the updated extra nonce. + block := btcutil.NewBlock(msgBlock) + merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] + return nil +} + +// BestSnapshot returns information about the current best chain block and +// related state as of the current point in time using the chain instance +// associated with the block template generator. The returned state must be +// treated as immutable since it is shared by all callers. +// +// This function is safe for concurrent access. +func (g *BlkTmplGenerator) BestSnapshot() *blockchain.BestState { + return g.chain.BestSnapshot() +} + +// TxSource returns the associated transaction source. +// +// This function is safe for concurrent access. +func (g *BlkTmplGenerator) TxSource() TxSource { + return g.txSource +} diff --git a/mining_test.go b/mining/mining_test.go similarity index 99% rename from mining_test.go rename to mining/mining_test.go index e0b5bd72..362253e5 100644 --- a/mining_test.go +++ b/mining/mining_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package mining import ( "container/heap" diff --git a/rpcserver.go b/rpcserver.go index c17e5281..b824c586 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -34,6 +34,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcd/mempool" + "github.com/btcsuite/btcd/mining" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" @@ -98,7 +99,8 @@ var ( // data. gbtCoinbaseAux = &btcjson.GetBlockTemplateResultAux{ Flags: hex.EncodeToString(builderScript(txscript. - NewScriptBuilder().AddData([]byte(coinbaseFlags)))), + NewScriptBuilder(). + AddData([]byte(mining.CoinbaseFlags)))), } // gbtCapabilities describes additional capabilities returned with a @@ -349,7 +351,7 @@ type gbtWorkState struct { lastGenerated time.Time prevHash *chainhash.Hash minTimestamp time.Time - template *BlockTemplate + template *mining.BlockTemplate notifyMap map[chainhash.Hash]map[int64]chan struct{} timeSource blockchain.MedianTimeSource } @@ -1398,11 +1400,11 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo targetDifficulty = fmt.Sprintf("%064x", blockchain.CompactToBig(msgBlock.Header.Bits)) - // Find the minimum allowed timestamp for the block based on the + // Get the minimum allowed timestamp for the block based on the // median timestamp of the last several blocks per the chain // consensus rules. best := s.server.blockManager.chain.BestSnapshot() - minTimestamp := minimumMedianTime(best) + minTimestamp := mining.MinimumMedianTime(best) // Update work state to ensure another block template isn't // generated until needed. @@ -3654,7 +3656,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{ type rpcServer struct { started int32 shutdown int32 - generator *BlkTmplGenerator + generator *mining.BlkTmplGenerator server *server chain *blockchain.BlockChain authsha [fastsha256.Size]byte @@ -4160,7 +4162,7 @@ func genCertPair(certFile, keyFile string) error { } // newRPCServer returns a new instance of the rpcServer struct. -func newRPCServer(listenAddrs []string, generator *BlkTmplGenerator, s *server) (*rpcServer, error) { +func newRPCServer(listenAddrs []string, generator *mining.BlkTmplGenerator, s *server) (*rpcServer, error) { rpc := rpcServer{ server: s, generator: generator, diff --git a/server.go b/server.go index cdb334b9..db9f5abf 100644 --- a/server.go +++ b/server.go @@ -2380,8 +2380,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param BlockPrioritySize: cfg.BlockPrioritySize, TxMinFreeFee: cfg.minRelayTxFee, } - blockTemplateGenerator := newBlkTmplGenerator(&policy, s.chainParams, - s.txMemPool, s.blockManager.chain, s.timeSource, s.sigCache) + blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, + s.chainParams, s.txMemPool, s.blockManager.chain, s.timeSource, + s.sigCache) s.cpuMiner = newCPUMiner(&cpuminerConfig{ ChainParams: chainParams, BlockTemplateGenerator: blockTemplateGenerator,