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 }