From 74fe2a4dfde5039b203559c147af665606ab082e Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 25 Oct 2016 18:52:24 -0500 Subject: [PATCH] mining: Introduce a block template generator. This introduces a new type named BlkTmplGenerator which encapsulates the various state needed to generate block templates. This is useful since it means code that needs to generate block templates can simply accept the generator rather than needing access to all of the additional state which in turn will ultimately make it easier to split the mining code into its own package. --- cpuminer.go | 33 +++++++++++++--------------- mining.go | 62 +++++++++++++++++++++++++++++++++++++++++----------- rpcserver.go | 18 +++++++-------- server.go | 11 +++++++--- 4 files changed, 81 insertions(+), 43 deletions(-) diff --git a/cpuminer.go b/cpuminer.go index ad0eb55a..ddaf3e0c 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -14,7 +14,6 @@ import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/mining" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -54,8 +53,7 @@ var ( // system which is typically sufficient. type CPUMiner struct { sync.Mutex - policy *mining.Policy - txSource mining.TxSource + g *BlkTmplGenerator server *server numWorkers uint32 started bool @@ -125,7 +123,7 @@ func (m *CPUMiner) submitBlock(block *btcutil.Block) bool { // detected and all work on the stale block is halted to start work on // a new block, but the check only happens periodically, so it is // possible a block was found and submitted in between. - latestHash := m.server.blockManager.chain.BestSnapshot().Hash + latestHash := m.g.blockManager.chain.BestSnapshot().Hash msgBlock := block.MsgBlock() if !msgBlock.Header.PrevBlock.IsEqual(latestHash) { minrLog.Debugf("Block submitted via CPU miner with previous "+ @@ -135,7 +133,7 @@ func (m *CPUMiner) submitBlock(block *btcutil.Block) bool { // Process this block using the same rules as blocks coming from other // nodes. This will in turn relay it to the network like normal. - isOrphan, err := m.server.blockManager.ProcessBlock(block, blockchain.BFNone) + isOrphan, err := m.g.blockManager.ProcessBlock(block, blockchain.BFNone) if err != nil { // Anything other than a rule violation is an unexpected error, // so log that error as an internal error. @@ -187,7 +185,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, // Initial state. lastGenerated := time.Now() - lastTxUpdate := m.txSource.LastUpdated() + lastTxUpdate := m.g.txSource.LastUpdated() hashesCompleted := uint64(0) // Note that the entire extra nonce range is iterated and the offset is @@ -197,7 +195,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, // Update the extra nonce in the block template with the // new value by regenerating the coinbase script and // setting the merkle root to the new value. The - UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) + m.g.UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) // Search through the entire nonce range for a solution while // periodically checking for early quit and stale block @@ -213,7 +211,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, // The current block is stale if the best block // has changed. - best := m.server.blockManager.chain.BestSnapshot() + best := m.g.blockManager.chain.BestSnapshot() if !header.PrevBlock.IsEqual(best.Hash) { return false } @@ -222,13 +220,13 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, // has been updated since the block template was // generated and it has been at least one // minute. - if lastTxUpdate != m.txSource.LastUpdated() && + if lastTxUpdate != m.g.txSource.LastUpdated() && time.Now().After(lastGenerated.Add(time.Minute)) { return false } - UpdateBlockTime(msgBlock, m.server.blockManager) + m.g.UpdateBlockTime(msgBlock) default: // Non-blocking select to fall through @@ -292,8 +290,8 @@ out: // this would otherwise end up building a new block template on // a block that is in the process of becoming stale. m.submitBlockLock.Lock() - curHeight := m.server.blockManager.chain.BestSnapshot().Height - if curHeight != 0 && !m.server.blockManager.IsCurrent() { + curHeight := m.g.blockManager.chain.BestSnapshot().Height + if curHeight != 0 && !m.g.blockManager.IsCurrent() { m.submitBlockLock.Unlock() time.Sleep(time.Second) continue @@ -306,7 +304,7 @@ out: // Create a new block template using the available transactions // in the memory pool as a source of transactions to potentially // include in the block. - template, err := NewBlockTemplate(m.policy, m.server, payToAddr) + template, err := m.g.NewBlockTemplate(payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ @@ -559,7 +557,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) { // be changing and this would otherwise end up building a new block // template on a block that is in the process of becoming stale. m.submitBlockLock.Lock() - curHeight := m.server.blockManager.chain.BestSnapshot().Height + curHeight := m.g.blockManager.chain.BestSnapshot().Height // Choose a payment address at random. rand.Seed(time.Now().UnixNano()) @@ -568,7 +566,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) { // Create a new block template using the available transactions // in the memory pool as a source of transactions to potentially // include in the block. - template, err := NewBlockTemplate(m.policy, m.server, payToAddr) + template, err := m.g.NewBlockTemplate(payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ @@ -603,10 +601,9 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) { // newCPUMiner returns a new instance of a CPU miner for the provided server. // Use Start to begin the mining process. See the documentation for CPUMiner // type for more details. -func newCPUMiner(policy *mining.Policy, s *server) *CPUMiner { +func newCPUMiner(generator *BlkTmplGenerator, s *server) *CPUMiner { return &CPUMiner{ - policy: policy, - txSource: s.txMemPool, + g: generator, server: s, numWorkers: defaultNumWorkers, updateNumWorkers: make(chan struct{}), diff --git a/mining.go b/mining.go index cd621003..63c2b80a 100644 --- a/mining.go +++ b/mining.go @@ -303,6 +303,40 @@ func medianAdjustedTime(chainState *blockchain.BestState, timeSource blockchain. 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 + txSource mining.TxSource + sigCache *txscript.SigCache + blockManager *blockManager + timeSource blockchain.MedianTimeSource +} + +// 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, txSource mining.TxSource, + timeSource blockchain.MedianTimeSource, sigCache *txscript.SigCache, + blockManager *blockManager) *BlkTmplGenerator { + + return &BlkTmplGenerator{ + policy: policy, + txSource: txSource, + sigCache: sigCache, + blockManager: blockManager, + timeSource: timeSource, + } +} + // 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 @@ -365,10 +399,12 @@ func medianAdjustedTime(chainState *blockchain.BestState, timeSource blockchain. // | transactions (while block size | | // | <= policy.BlockMinSize) | | // ----------------------------------- -- -func NewBlockTemplate(policy *mining.Policy, server *server, payToAddress btcutil.Address) (*BlockTemplate, error) { - var txSource mining.TxSource = server.txMemPool - blockManager := server.blockManager - timeSource := server.timeSource +func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*BlockTemplate, error) { + // Locals for faster access. + policy := g.policy + blockManager := g.blockManager + timeSource := g.timeSource + sigCache := g.sigCache // Extend the most recently known best block. best := blockManager.chain.BestSnapshot() @@ -401,7 +437,7 @@ func NewBlockTemplate(policy *mining.Policy, server *server, payToAddress btcuti // 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 := txSource.MiningDescs() + sourceTxns := g.txSource.MiningDescs() sortedByFee := policy.BlockPrioritySize == 0 priorityQueue := newTxPriorityQueue(len(sourceTxns), sortedByFee) @@ -471,7 +507,7 @@ mempoolLoop: originIndex := txIn.PreviousOutPoint.Index utxoEntry := utxos.LookupEntry(originHash) if utxoEntry == nil || utxoEntry.IsOutputSpent(originIndex) { - if !txSource.HaveTransaction(originHash) { + if !g.txSource.HaveTransaction(originHash) { minrLog.Tracef("Skipping tx %s because "+ "it references unspent output "+ "%s which is not available", @@ -636,7 +672,7 @@ mempoolLoop: continue } err = blockchain.ValidateTransactionScripts(tx, blockUtxos, - txscript.StandardVerifyFlags, server.sigCache) + txscript.StandardVerifyFlags, sigCache) if err != nil { minrLog.Tracef("Skipping tx %s due to error in "+ "ValidateTransactionScripts: %v", tx.Hash(), err) @@ -738,19 +774,19 @@ mempoolLoop: // 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 UpdateBlockTime(msgBlock *wire.MsgBlock, bManager *blockManager) error { +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. - best := bManager.chain.BestSnapshot() - newTimestamp := medianAdjustedTime(best, bManager.server.timeSource) + best := g.blockManager.chain.BestSnapshot() + newTimestamp := medianAdjustedTime(best, g.timeSource) msgBlock.Header.Timestamp = newTimestamp // If running on a network that requires recalculating the difficulty, // do so now. if activeNetParams.ReduceMinDifficulty { - difficulty, err := bManager.chain.CalcNextRequiredDifficulty( - newTimestamp) + chain := g.blockManager.chain + difficulty, err := chain.CalcNextRequiredDifficulty(newTimestamp) if err != nil { return err } @@ -764,7 +800,7 @@ func UpdateBlockTime(msgBlock *wire.MsgBlock, bManager *blockManager) error { // 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 UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight int32, extraNonce uint64) error { +func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight int32, extraNonce uint64) error { coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) if err != nil { return err diff --git a/rpcserver.go b/rpcserver.go index 9f638e18..c17e5281 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -34,7 +34,6 @@ 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" @@ -1389,7 +1388,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // block template doesn't include the coinbase, so the caller // will ultimately create their own coinbase which pays to the // appropriate address(es). - blkTemplate, err := NewBlockTemplate(s.policy, s.server, payAddr) + blkTemplate, err := s.generator.NewBlockTemplate(payAddr) if err != nil { return internalRPCError("Failed to create new block "+ "template: "+err.Error(), "") @@ -1462,7 +1461,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // Update the time of the block template to the current time // while accounting for the median time of the past several // blocks per the chain consensus rules. - UpdateBlockTime(msgBlock, s.server.blockManager) + s.generator.UpdateBlockTime(msgBlock) msgBlock.Header.Nonce = 0 rpcsLog.Debugf("Updated block template (timestamp %v, "+ @@ -2558,7 +2557,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // Choose a payment address at random. payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - template, err := NewBlockTemplate(s.policy, s.server, payToAddr) + template, err := s.generator.NewBlockTemplate(payToAddr) if err != nil { context := "Failed to create new block template" return nil, internalRPCError(err.Error(), context) @@ -2591,13 +2590,14 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // Update the time of the block template to the current time // while accounting for the median time of the past several // blocks per the chain consensus rules. - UpdateBlockTime(msgBlock, s.server.blockManager) + s.generator.UpdateBlockTime(msgBlock) // Increment the extra nonce and update the block template // with the new value by regenerating the coinbase script and // setting the merkle root to the new value. state.extraNonce++ - err := UpdateExtraNonce(msgBlock, latestHeight+1, state.extraNonce) + err := s.generator.UpdateExtraNonce(msgBlock, latestHeight+1, + state.extraNonce) if err != nil { errStr := fmt.Sprintf("Failed to update extra nonce: "+ "%v", err) @@ -3654,7 +3654,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{ type rpcServer struct { started int32 shutdown int32 - policy *mining.Policy + generator *BlkTmplGenerator server *server chain *blockchain.BlockChain authsha [fastsha256.Size]byte @@ -4160,10 +4160,10 @@ func genCertPair(certFile, keyFile string) error { } // newRPCServer returns a new instance of the rpcServer struct. -func newRPCServer(listenAddrs []string, policy *mining.Policy, s *server) (*rpcServer, error) { +func newRPCServer(listenAddrs []string, generator *BlkTmplGenerator, s *server) (*rpcServer, error) { rpc := rpcServer{ - policy: policy, server: s, + generator: generator, chain: s.blockManager.chain, statusLines: make(map[int]string), workState: newWorkState(), diff --git a/server.go b/server.go index 19413a78..df55b034 100644 --- a/server.go +++ b/server.go @@ -2365,7 +2365,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param } s.txMemPool = mempool.New(&txC) - // Create the mining policy based on the configuration options. + // Create the mining policy and block template generator based on the + // configuration options. + // // NOTE: The CPU miner relies on the mempool, so the mempool has to be // created before calling the function to create the CPU miner. policy := mining.Policy{ @@ -2374,7 +2376,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param BlockPrioritySize: cfg.BlockPrioritySize, TxMinFreeFee: cfg.minRelayTxFee, } - s.cpuMiner = newCPUMiner(&policy, &s) + blockTemplateGenerator := newBlkTmplGenerator(&policy, s.txMemPool, + s.timeSource, s.sigCache, bm) + s.cpuMiner = newCPUMiner(blockTemplateGenerator, &s) // Only setup a function to return new addresses to connect to when // not running in connect-only mode. The simulation network is always @@ -2449,7 +2453,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param } if !cfg.DisableRPC { - s.rpcServer, err = newRPCServer(cfg.RPCListeners, &policy, &s) + s.rpcServer, err = newRPCServer(cfg.RPCListeners, + blockTemplateGenerator, &s) if err != nil { return nil, err }