From 792fd3f6efe3b2d3819b8080460801e338fa7ccd Mon Sep 17 00:00:00 2001 From: Donald Adu-Poku Date: Thu, 16 Aug 2018 23:02:33 +0000 Subject: [PATCH] multi: automate block template regeneration. --- blockmanager.go | 269 ++++++++++++------ cpuminer.go | 93 ++---- mempool/mempool.go | 23 ++ mempool/mempool_test.go | 2 + mining.go | 86 ++---- rpcserver.go | 610 ++++++++++++++++++++-------------------- rpcserver_test.go | 4 +- server.go | 11 +- 8 files changed, 572 insertions(+), 526 deletions(-) diff --git a/blockmanager.go b/blockmanager.go index 0f11bf1a46..5663c308b4 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -67,6 +67,15 @@ const ( // otherwise arise from sending old orphan blocks and forcing nodes to // do expensive lottery data calculations for them. maxReorgDepthNotify = 6 + + // templateRegenSeconds is the required number of seconds elapsed, + // with incoming non vote transactions, before template + // regeneration is required. + templateRegenSeconds = 30 + + // maxTemplateRegenSeconds is the maximum number of seconds elapsed + // without incoming transactions before template regeneration is required. + maxTemplateRegenSeconds = 60 ) // zeroHash is the zero value hash (all zeros). It is defined as a convenience. @@ -235,6 +244,13 @@ type processTransactionMsg struct { reply chan processTransactionResponse } +// regenMsg handles a request for block template regeneration. +type regenMsg struct{} + +// nonVoteMsg handles a request signalling the receipt of a non vote +// transaction. +type nonVoteMsg struct{} + // isCurrentMsg is a message type to be sent across the message channel for // requesting whether or not the block manager believes it is synced with // the currently connected peers. @@ -242,53 +258,6 @@ type isCurrentMsg struct { reply chan bool } -// getCurrentTemplateMsg handles a request for the current mining block template. -type getCurrentTemplateMsg struct { - reply chan getCurrentTemplateResponse -} - -// getCurrentTemplateResponse is a response sent to the reply channel of a -// getCurrentTemplateMsg. -type getCurrentTemplateResponse struct { - Template *BlockTemplate -} - -// setCurrentTemplateMsg handles a request to change the current mining block -// template. -type setCurrentTemplateMsg struct { - Template *BlockTemplate - reply chan setCurrentTemplateResponse -} - -// setCurrentTemplateResponse is a response sent to the reply channel of a -// setCurrentTemplateMsg. -type setCurrentTemplateResponse struct { -} - -// getParentTemplateMsg handles a request for the current parent mining block -// template. -type getParentTemplateMsg struct { - reply chan getParentTemplateResponse -} - -// getParentTemplateResponse is a response sent to the reply channel of a -// getParentTemplateMsg. -type getParentTemplateResponse struct { - Template *BlockTemplate -} - -// setParentTemplateMsg handles a request to change the parent mining block -// template. -type setParentTemplateMsg struct { - Template *BlockTemplate - reply chan setParentTemplateResponse -} - -// setParentTemplateResponse is a response sent to the reply channel of a -// setParentTemplateMsg. -type setParentTemplateResponse struct { -} - // headerNode is used as a node in a list of headers that are linked together // between checkpoints. type headerNode struct { @@ -407,6 +376,18 @@ type blockManager struct { lotteryDataBroadcast map[chainhash.Hash]struct{} lotteryDataBroadcastMutex sync.RWMutex + // the following fields handle block template regeneration and updating + // registered clients. + mining bool + lastRegen time.Time + nonVoteTxCountLastInterval uint64 + nonVoteTxCount uint64 + registeredClients map[chan *BlockTemplate]struct{} + regenMtx sync.Mutex + templateMtx sync.RWMutex + nonVoteTxCountMtx sync.RWMutex + registerMtx sync.RWMutex + cachedCurrentTemplate *BlockTemplate cachedParentTemplate *BlockTemplate AggressiveMining bool @@ -764,12 +745,41 @@ func (b *blockManager) current() bool { return true } +// NotifyRegisteredTemplateClients broadcasts the provided block template +// to all registered clients. +func (b *blockManager) NotifyRegisteredTemplateClients(t *BlockTemplate) { + b.registerMtx.Lock() + for c := range b.registeredClients { + c <- t + // Delete the client after updating it. + delete(b.registeredClients, c) + } + b.registerMtx.Unlock() +} + +// RequestTemplateUpdate registers a client for block template updates. +func (b *blockManager) RequestTemplateUpdates() chan *BlockTemplate { + updateChan := make(chan *BlockTemplate) + b.registerMtx.Lock() + b.registeredClients[updateChan] = struct{}{} + b.registerMtx.Unlock() + return updateChan +} + +func (b *blockManager) LockTemplate() { + b.templateMtx.Lock() +} + +func (b *blockManager) UnlockTemplate() { + b.templateMtx.Unlock() +} + // checkBlockForHiddenVotes checks to see if a newly added block contains // any votes that were previously unknown to our daemon. If it does, it // adds these votes to the cached parent block template. // // This is UNSAFE for concurrent access. It must be called in single threaded -// access through the block mananger. All template access must also be routed +// access through the block manager. All template access must also be routed // through the block manager. func (b *blockManager) checkBlockForHiddenVotes(block *dcrutil.Block) { var votesFromBlock []*dcrutil.Tx @@ -783,6 +793,7 @@ func (b *blockManager) checkBlockForHiddenVotes(block *dcrutil.Block) { // the parent template hasn't yet been updated, so we may // need to use the current template. var template *BlockTemplate + b.templateMtx.RLock() if b.cachedCurrentTemplate != nil { if b.cachedCurrentTemplate.Height == block.Height() { @@ -796,6 +807,7 @@ func (b *blockManager) checkBlockForHiddenVotes(block *dcrutil.Block) { template = b.cachedParentTemplate } } + b.templateMtx.RUnlock() // No template to alter. if template == nil { @@ -1592,6 +1604,15 @@ func (b *blockManager) limitMap(m map[chainhash.Hash]struct{}, limit int) { // the fetching should proceed. func (b *blockManager) blockHandler() { candidatePeers := list.New() + b.lastRegen = b.server.txMemPool.LastUpdated() + updateNonVoteCounters := func() { + b.nonVoteTxCountMtx.Lock() + b.nonVoteTxCountLastInterval, b.nonVoteTxCount = b.nonVoteTxCount, 0 + b.nonVoteTxCountMtx.Unlock() + } + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + out: for { select { @@ -1805,31 +1826,107 @@ out: case isCurrentMsg: msg.reply <- b.current() - case getCurrentTemplateMsg: - cur := deepCopyBlockTemplate(b.cachedCurrentTemplate) - msg.reply <- getCurrentTemplateResponse{ - Template: cur, + case nonVoteMsg: + if b.mining { + // Increment the non vote transaction counter. + b.nonVoteTxCountMtx.Lock() + b.nonVoteTxCount++ + b.nonVoteTxCountMtx.Unlock() } - case setCurrentTemplateMsg: - b.cachedCurrentTemplate = deepCopyBlockTemplate(msg.Template) - msg.reply <- setCurrentTemplateResponse{} - - case getParentTemplateMsg: - par := deepCopyBlockTemplate(b.cachedParentTemplate) - msg.reply <- getParentTemplateResponse{ - Template: par, - } - - case setParentTemplateMsg: - b.cachedParentTemplate = deepCopyBlockTemplate(msg.Template) - msg.reply <- setParentTemplateResponse{} + case regenMsg: + go func() { + // Regenerate a template the chain is either current or + // is due its first block (height = 0) and there is at + // least one registered subscriber for block template + // updates. + + b.registerMtx.Lock() + hasRegisteredClients := len(b.registeredClients) > 0 + b.registerMtx.Unlock() + + if b.mining && (b.IsCurrent() || + b.chainState.newestHeight == 0) && + hasRegisteredClients { + + // Pick a mining address at random. + rand.Seed(time.Now().UnixNano()) + payToAddr := + cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + + // Regenerate the block template. + b.templateMtx.Lock() + template, err := + NewBlockTemplate(b.server.rpcServer.policy, b.server, + payToAddr) + if err != nil { + bmgrLog.Infof( + "block template generation failed: %v", err) + } + if template != nil { + // Update the current and parent templates and + // notify all registered template clients. + b.cachedParentTemplate, b.cachedCurrentTemplate = + b.cachedCurrentTemplate, template + b.NotifyRegisteredTemplateClients(b.cachedCurrentTemplate) + bmgrLog.Debugf("block template generated "+ + "(height: %v, hash: %v)", + template.Block.Header.Height, + template.Block.BlockHash()) + + // Update the last regen time. + b.regenMtx.Lock() + b.lastRegen = time.Now() + b.regenMtx.Unlock() + } + b.templateMtx.Unlock() + } + }() default: bmgrLog.Warnf("Invalid message type in block "+ "handler: %T", msg) } + case <-ticker.C: + go func() { + if b.mining && b.IsCurrent() { + b.registerMtx.RLock() + hasRegisteredClients := len(b.registeredClients) > 0 + b.registerMtx.RUnlock() + + b.nonVoteTxCountMtx.RLock() + hasNewNonVoteTxs := b.nonVoteTxCount > 0 + b.nonVoteTxCountMtx.RUnlock() + + b.regenMtx.Lock() + timeDiff := time.Since(b.lastRegen) + regen := b.server.txMemPool.LastUpdated().After(b.lastRegen. + Add(templateRegenSeconds * time.Second)) + b.regenMtx.Unlock() + + // Signal template regeneration if a minute has elapsed + // since the last regeneration and there is at least one + // registered subscriber for block template updates. + if (timeDiff.Seconds() > maxTemplateRegenSeconds) && + hasRegisteredClients { + b.RegenTemplate() + updateNonVoteCounters() + return + } + + // Signal template regeneration if thirty seconds have + // elapsed and at least a new non vote transaction has + // been accepted by the mempool nd there is at least one + // registered subscriber for block template updates. + if regen && hasNewNonVoteTxs && hasRegisteredClients { + b.RegenTemplate() + } + + updateNonVoteCounters() + } + }() + case <-b.quit: break out } @@ -2119,7 +2216,9 @@ func (b *blockManager) handleNotifyMsg(notification *blockchain.Notification) { // Drop the associated mining template from the old chain, since it // will be no longer valid. + b.templateMtx.Lock() b.cachedCurrentTemplate = nil + b.templateMtx.Unlock() } } @@ -2413,39 +2512,19 @@ func (b *blockManager) TicketPoolValue() (dcrutil.Amount, error) { return b.chain.TicketPoolValue() } -// GetCurrentTemplate gets the current block template for mining. -func (b *blockManager) GetCurrentTemplate() *BlockTemplate { - reply := make(chan getCurrentTemplateResponse) - b.msgChan <- getCurrentTemplateMsg{reply: reply} - response := <-reply - return response.Template -} - -// SetCurrentTemplate sets the current block template for mining. -func (b *blockManager) SetCurrentTemplate(bt *BlockTemplate) { - reply := make(chan setCurrentTemplateResponse) - b.msgChan <- setCurrentTemplateMsg{Template: bt, reply: reply} - <-reply -} - -// GetParentTemplate gets the current parent block template for mining. -func (b *blockManager) GetParentTemplate() *BlockTemplate { - reply := make(chan getParentTemplateResponse) - b.msgChan <- getParentTemplateMsg{reply: reply} - response := <-reply - return response.Template +// RegenTemplate regenerates the block template. +func (b *blockManager) RegenTemplate() { + b.msgChan <- regenMsg{} } -// SetParentTemplate sets the current parent block template for mining. -func (b *blockManager) SetParentTemplate(bt *BlockTemplate) { - reply := make(chan setParentTemplateResponse) - b.msgChan <- setParentTemplateMsg{Template: bt, reply: reply} - <-reply +// ReceiveNonVoteTx signals the receipt of a non vote transaction. +func (b *blockManager) ReceiveNonVoteTx() { + b.msgChan <- nonVoteMsg{} } // newBlockManager returns a new Decred block manager. // Use Start to begin processing asynchronous block and inv updates. -func newBlockManager(s *server, indexManager blockchain.IndexManager, interrupt <-chan struct{}) (*blockManager, error) { +func newBlockManager(s *server, indexManager blockchain.IndexManager, mining bool, interrupt <-chan struct{}) (*blockManager, error) { bm := blockManager{ server: s, rejectedTxns: make(map[chainhash.Hash]struct{}), @@ -2456,6 +2535,12 @@ func newBlockManager(s *server, indexManager blockchain.IndexManager, interrupt progressLogger: newBlockProgressLogger("Processed", bmgrLog), msgChan: make(chan interface{}, cfg.MaxPeers*3), headerList: list.New(), + mining: mining, + regenMtx: sync.Mutex{}, + templateMtx: sync.RWMutex{}, + nonVoteTxCountMtx: sync.RWMutex{}, + registerMtx: sync.RWMutex{}, + registeredClients: make(map[chan *BlockTemplate]struct{}), AggressiveMining: !cfg.NonAggressive, quit: make(chan struct{}), } diff --git a/cpuminer.go b/cpuminer.go index dcbeac2979..7de62fc8ad 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -8,8 +8,6 @@ package main import ( "encoding/binary" "errors" - "fmt" - "math/rand" "sync" "time" @@ -192,7 +190,8 @@ func (m *CPUMiner) submitBlock(block *dcrutil.Block) bool { // This function will return early with false when conditions that trigger a // stale block such as a new block showing up or periodically when there are // new transactions and enough time has elapsed without finding a solution. -func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit chan struct{}) bool { +// This should be called with the +func (m *CPUMiner) solveBlock(header *wire.BlockHeader, ticker *time.Ticker, quit chan struct{}) bool { // Choose a random extra nonce offset for this block template and // worker. enOffset, err := wire.RandomUint64() @@ -203,7 +202,6 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit } // Create a couple of convenience variables. - header := &msgBlock.Header targetDifficulty := blockchain.CompactToBig(header.Bits) // Initial state. @@ -242,7 +240,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit return false } - err = UpdateBlockTime(msgBlock, m.server.blockManager) + err = UpdateBlockTime(header, m.server.blockManager) if err != nil { minrLog.Warnf("CPU miner unable to update block template "+ "time: %v", err) @@ -295,14 +293,6 @@ out: // Non-blocking select to fall through } - // No point in searching for a solution before the chain is - // synced. Also, grab the same lock as used for block - // submission, since the current block will 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() - time.Sleep(100 * time.Millisecond) - // Hacks to make dcr work with Decred PoC (simnet only) // TODO Remove before production. if cfg.SimNet { @@ -317,26 +307,16 @@ out: } } - // Choose a payment address at random. - rand.Seed(time.Now().UnixNano()) - payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - - // 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) - m.submitBlockLock.Unlock() - if err != nil { - errStr := fmt.Sprintf("Failed to create new block "+ - "template: %v", err) - minrLog.Errorf(errStr) - continue - } + updateChan := m.server.blockManager.RequestTemplateUpdates() + template := <-updateChan + minrLog.Debugf("block template received "+ + "(height: %v, hash: %v)", + template.Block.Header.Height, + template.Block.BlockHash()) - // Not enough voters. - if template == nil { - continue - } + m.server.blockManager.templateMtx.RLock() + header := template.Block.Header + m.server.blockManager.templateMtx.RUnlock() // This prevents you from causing memory exhaustion issues // when mining aggressively in a simulation network. @@ -354,7 +334,7 @@ out: // with false when conditions that trigger a stale block, so // a new block template can be generated. When the return is // true a solution was found, so submit the solved block. - if m.solveBlock(template.Block, ticker, quit) { + if m.solveBlock(&header, ticker, quit) { block := dcrutil.NewBlock(template.Block) m.submitBlock(block) m.minedOnParents[template.Block.Header.PrevBlock]++ @@ -591,42 +571,29 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) { default: } - // Grab the lock used for block submission, since the current block will - // 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() - - // Choose a payment address at random. - rand.Seed(time.Now().UnixNano()) - payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - - // 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) - m.submitBlockLock.Unlock() - if err != nil { - errStr := fmt.Sprintf("Failed to create new block "+ - "template: %v", err) - minrLog.Errorf(errStr) - continue - } - if template == nil { - errStr := fmt.Sprintf("Not enough voters on parent block " + - "and failed to pull parent template") - minrLog.Debugf(errStr) - continue - } + updateChan := m.server.blockManager.RequestTemplateUpdates() + m.server.blockManager.RegenTemplate() + template := <-updateChan + minrLog.Debugf("block template received "+ + "(height: %v, hash: %v)", + template.Block.Header.Height, + template.Block.BlockHash()) + + m.server.blockManager.templateMtx.RLock() + header := template.Block.Header + m.server.blockManager.templateMtx.RUnlock() // Attempt to solve the block. The function will exit early // with false when conditions that trigger a stale block, so // a new block template can be generated. When the return is // true a solution was found, so submit the solved block. - if m.solveBlock(template.Block, ticker, nil) { + if m.solveBlock(&header, ticker, nil) { block := dcrutil.NewBlock(template.Block) - m.submitBlock(block) - blockHashes[i] = block.Hash() - i++ + accepted := m.submitBlock(block) + if accepted { + blockHashes[i] = block.Hash() + i++ + } if i == n { minrLog.Tracef("Generated %d blocks", i) m.Lock() diff --git a/mempool/mempool.go b/mempool/mempool.go index f2985b7b8e..22428e8501 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -120,6 +120,14 @@ type Config struct { // to use for indexing the unconfirmed transactions in the memory pool. // This can be nil if the address index is not enabled. ExistsAddrIndex *indexers.ExistsAddrIndex + + // RegenTemplate defines the function to use in regenerating a + // block template. + RegenTemplate func() + + // NonVoteTx defines the function to use in signalling the receipt of a non + // vota transaction. + NonVoteTx func() } // Policy houses the policy (configuration parameters) which is used to @@ -640,6 +648,21 @@ func (mp *TxPool) RemoveDoubleSpends(tx *dcrutil.Tx) { // This function MUST be called with the mempool lock held (for writes). func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *dcrutil.Tx, txType stake.TxType, height int64, fee int64) { + // Signal block template regeneration when a vote transaction is received + // or a non vote transaction if the received transaction is not a vote. + if txType == stake.TxTypeSSGen { + if mp.cfg.RegenTemplate != nil { + mp.cfg.RegenTemplate() + } else { + log.Debug("provided regen template function is nil") + } + } else { + if mp.cfg.NonVoteTx != nil { + mp.cfg.NonVoteTx() + } else { + log.Debug("provided non vote tx function is nil") + } + } // Add the transaction to the pool and mark the referenced outpoints // as spent by the pool. diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 158705178c..57aa689e45 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -611,6 +611,8 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp SigCache: nil, AddrIndex: nil, ExistsAddrIndex: nil, + RegenTemplate: nil, + NonVoteTx: nil, }), } diff --git a/mining.go b/mining.go index e20045fb30..aaa89b1fdf 100644 --- a/mining.go +++ b/mining.go @@ -760,24 +760,23 @@ func deepCopyBlockTemplate(blockTemplate *BlockTemplate) *BlockTemplate { // of the blockchain. If there are too few voters and a cached parent template to // work off of is present, it will return a copy of that template to pass to the // miner. -// Safe for concurrent access. +// This should be called with the template mutex held. func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, miningAddress dcrutil.Address, bm *blockManager) (*BlockTemplate, error) { timeSource := bm.server.timeSource chainState := &bm.chainState stakeValidationHeight := bm.server.chainParams.StakeValidationHeight - curTemplate := bm.GetCurrentTemplate() + template := bm.cachedCurrentTemplate // Check to see if we've fallen off the chain, for example if a // reorganization had recently occurred. If this is the case, // nuke the templates. prevBlockHash := chainState.GetTopPrevHash() - if curTemplate != nil { - if !prevBlockHash.IsEqual( - &curTemplate.Block.Header.PrevBlock) { + if template != nil { + if !prevBlockHash.IsEqual(&(template.Block.Header.PrevBlock)) { minrLog.Debugf("Cached mining templates are no longer current, " + "resetting") - bm.SetCurrentTemplate(nil) - bm.SetParentTemplate(nil) + bm.cachedCurrentTemplate = nil + bm.cachedParentTemplate = nil } } @@ -785,20 +784,18 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, // (default behaviour). if nextHeight >= stakeValidationHeight { if bm.AggressiveMining { - if curTemplate != nil { - cptCopy := deepCopyBlockTemplate(curTemplate) - + if template != nil { // Update the timestamp of the old template. ts, err := medianAdjustedTime(chainState, timeSource) if err != nil { return nil, err } - cptCopy.Block.Header.Timestamp = ts + template.Block.Header.Timestamp = ts // If we're on testnet, the time since this last block // listed as the parent must be taken into consideration. if bm.server.chainParams.ReduceMinDifficulty { - parentHash := cptCopy.Block.Header.PrevBlock + parentHash := template.Block.Header.PrevBlock requiredDifficulty, err := bm.CalcNextRequiredDiffNode(&parentHash, ts) @@ -807,27 +804,27 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, err.Error()) } - cptCopy.Block.Header.Bits = requiredDifficulty + template.Block.Header.Bits = requiredDifficulty } // Choose a new extra nonce value that is one greater // than the previous extra nonce, so we don't remine the // same block and choose the same winners as before. - en := cptCopy.extractCoinbaseExtraNonce() + 1 - err = UpdateExtraNonce(cptCopy.Block, cptCopy.Height, en) + en := template.extractCoinbaseExtraNonce() + 1 + err = UpdateExtraNonce(template.Block, template.Height, en) if err != nil { return nil, err } // Update extranonce of the original template too, so // we keep getting unique numbers. - err = UpdateExtraNonce(curTemplate.Block, curTemplate.Height, en) + err = UpdateExtraNonce(template.Block, template.Height, en) if err != nil { return nil, err } // Make sure the block validates. - block := dcrutil.NewBlockDeepCopyCoinbase(cptCopy.Block) + block := dcrutil.NewBlockDeepCopyCoinbase(template.Block) err = bm.chain.CheckConnectBlockTemplate(block) if err != nil { minrLog.Errorf("failed to check template while "+ @@ -836,12 +833,12 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, err.Error()) } - return cptCopy, nil + return template, nil } // We may have just started mining and stored the current block // template, so we don't have a parent. - if curTemplate == nil { + if template == nil { // Fetch the latest block and head and begin working // off of it with an empty transaction tree regular // and the contents of that stake tree. In the future @@ -939,10 +936,7 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, str) } - // Make a copy to return. - cptCopy := deepCopyBlockTemplate(bt) - - return cptCopy, nil + return bt, nil } } } @@ -953,43 +947,6 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, return nil, nil } -// handleCreatedBlockTemplate stores a successfully created block template to -// the appropriate cache if needed, then returns the template to the miner to -// work on. The stored template is a copy of the template, to prevent races -// from occurring in case the template is mined on by the CPUminer. -func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, bm *blockManager) (*BlockTemplate, error) { - curTemplate := bm.GetCurrentTemplate() - - nextBlockHeight := blockTemplate.Height - stakeValidationHeight := bm.server.chainParams.StakeValidationHeight - // This is where we begin storing block templates, when either the - // program is freshly started or the chain is matured to stake - // validation height. - if curTemplate == nil && nextBlockHeight >= stakeValidationHeight-2 { - bm.SetCurrentTemplate(blockTemplate) - } - - // We're at the height where the next block needs to include SSGens, - // so we check to if CachedCurrentTemplate is out of date. If it is, - // we store it as the cached parent template, and store the new block - // template as the currenct template. - if curTemplate != nil && nextBlockHeight >= stakeValidationHeight-1 { - if curTemplate.Height < nextBlockHeight { - bm.SetParentTemplate(curTemplate) - bm.SetCurrentTemplate(blockTemplate) - } - } - - // Overwrite the old cached block if it's out of date. - if curTemplate != nil { - if curTemplate.Height == nextBlockHeight { - bm.SetCurrentTemplate(blockTemplate) - } - } - - return blockTemplate, nil -} - // 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 @@ -2040,7 +1997,8 @@ mempoolLoop: ValidPayAddress: payToAddress != nil, } - return handleCreatedBlockTemplate(blockTemplate, server.blockManager) + return blockTemplate, nil + // return handleCreatedBlockTemplate(blockTemplate, server.blockManager) } // UpdateBlockTime updates the timestamp in the header of the passed block to @@ -2049,7 +2007,7 @@ 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 UpdateBlockTime(header *wire.BlockHeader, bManager *blockManager) 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. @@ -2058,7 +2016,7 @@ func UpdateBlockTime(msgBlock *wire.MsgBlock, bManager *blockManager) error { if err != nil { return miningRuleError(ErrGettingMedianTime, err.Error()) } - msgBlock.Header.Timestamp = newTimestamp + header.Timestamp = newTimestamp // If running on a network that requires recalculating the difficulty, // do so now. @@ -2068,7 +2026,7 @@ func UpdateBlockTime(msgBlock *wire.MsgBlock, bManager *blockManager) error { if err != nil { return miningRuleError(ErrGettingDifficulty, err.Error()) } - msgBlock.Header.Bits = difficulty + header.Bits = difficulty } return nil diff --git a/rpcserver.go b/rpcserver.go index 4c64c7511a..c3ef13e954 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2343,136 +2343,136 @@ func (state *gbtWorkState) templateUpdateChan(prevHash *chainhash.Hash, lastGene // // This function MUST be called with the state locked. func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error { - lastTxUpdate := s.server.txMemPool.LastUpdated() - if lastTxUpdate.IsZero() { - lastTxUpdate = time.Now() - } - - // Generate a new block template when the current best block has - // changed or the transactions in the memory pool have been updated and - // it has been at least gbtRegenerateSecond since the last template was - // generated. - var msgBlock *wire.MsgBlock - var targetDifficulty string - latestHash, _ := s.server.blockManager.chainState.Best() - template := state.template - if template == nil || state.prevHash == nil || - !state.prevHash.IsEqual(latestHash) || - (state.lastTxUpdate != lastTxUpdate && - time.Now().After(state.lastGenerated.Add(time.Second* - gbtRegenerateSeconds))) { - - // Reset the previous best hash the block template was generated - // against so any errors below cause the next invocation to try - // again. - state.prevHash = nil - - // Choose a payment address at random if the caller requests a - // full coinbase as opposed to only the pertinent details needed - // to create their own coinbase. - var payAddr dcrutil.Address - if !useCoinbaseValue { - payAddr = cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - } - - // Create a new block template that has a coinbase which anyone - // can redeem. This is only acceptable because the returned - // 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) - if err != nil { - return rpcInternalError("Failed to create new block "+ - "template: "+err.Error(), "") - } - if blkTemplate == nil { - return rpcInternalError("Failed to create new block "+ - "template: not enough voters on parent and no "+ - "suitable cached template", "") - } - template = blkTemplate - msgBlock = template.Block - targetDifficulty = fmt.Sprintf("%064x", - blockchain.CompactToBig(msgBlock.Header.Bits)) - - // Find the minimum allowed timestamp for the block based on the - // median timestamp of the last several blocks per the chain - // consensus rules. - chainState := &s.server.blockManager.chainState - minTimestamp, err := minimumMedianTime(chainState) - if err != nil { - context := "Failed to get minimum median time" - return rpcInternalError(err.Error(), context) - } - - // Update work state to ensure another block template isn't - // generated until needed. - state.template = deepCopyBlockTemplate(template) - state.lastGenerated = time.Now() - state.lastTxUpdate = lastTxUpdate - state.prevHash = latestHash - state.minTimestamp = minTimestamp - - rpcsLog.Debugf("Generated block template (timestamp %v, "+ - "target %s, merkle root %s)", - msgBlock.Header.Timestamp, targetDifficulty, - msgBlock.Header.MerkleRoot) - - // Notify any clients that are long polling about the new - // template. - state.notifyLongPollers(latestHash, lastTxUpdate) - } else { - // At this point, there is a saved block template and another - // request for a template was made, but either the available - // transactions haven't change or it hasn't been long enough to - // trigger a new block template to be generated. So, update the - // existing block template. - - // When the caller requires a full coinbase as opposed to only - // the pertinent details needed to create their own coinbase, - // add a payment address to the output of the coinbase of the - // template if it doesn't already have one. Since this requires - // mining addresses to be specified via the config, an error is - // returned if none have been specified. - if !useCoinbaseValue && !template.ValidPayAddress { - // Choose a payment address at random. - payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - - // Update the block coinbase output of the template to - // pay to the randomly selected payment address. - pkScript, err := txscript.PayToAddrScript(payToAddr) - if err != nil { - context := "Failed to create pay-to-addr script" - return rpcInternalError(err.Error(), context) - } - template.Block.Transactions[0].TxOut[0].PkScript = pkScript - template.ValidPayAddress = true - - // Update the merkle root. - block := dcrutil.NewBlock(template.Block) - merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) - template.Block.Header.MerkleRoot = *merkles[len(merkles)-1] - } - - // Set locals for convenience. - msgBlock = template.Block - targetDifficulty = fmt.Sprintf("%064x", - blockchain.CompactToBig(msgBlock.Header.Bits)) - - // 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. - err := UpdateBlockTime(msgBlock, s.server.blockManager) - if err != nil { - context := "Failed to update timestamp" - return rpcInternalError(err.Error(), context) - } - msgBlock.Header.Nonce = 0 - - rpcsLog.Debugf("Updated block template (timestamp %v, "+ - "target %s)", msgBlock.Header.Timestamp, - targetDifficulty) - } + // lastTxUpdate := s.server.txMemPool.LastUpdated() + // if lastTxUpdate.IsZero() { + // lastTxUpdate = time.Now() + // } + + // // Generate a new block template when the current best block has + // // changed or the transactions in the memory pool have been updated and + // // it has been at least gbtRegenerateSecond since the last template was + // // generated. + // var msgBlock *wire.MsgBlock + // var targetDifficulty string + // latestHash, _ := s.server.blockManager.chainState.Best() + // template := state.template + // if template == nil || state.prevHash == nil || + // !state.prevHash.IsEqual(latestHash) || + // (state.lastTxUpdate != lastTxUpdate && + // time.Now().After(state.lastGenerated.Add(time.Second* + // gbtRegenerateSeconds))) { + + // // Reset the previous best hash the block template was generated + // // against so any errors below cause the next invocation to try + // // again. + // state.prevHash = nil + + // // Choose a payment address at random if the caller requests a + // // full coinbase as opposed to only the pertinent details needed + // // to create their own coinbase. + // var payAddr dcrutil.Address + // if !useCoinbaseValue { + // payAddr = cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + // } + + // // Create a new block template that has a coinbase which anyone + // // can redeem. This is only acceptable because the returned + // // 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) + // if err != nil { + // return rpcInternalError("Failed to create new block "+ + // "template: "+err.Error(), "") + // } + // if blkTemplate == nil { + // return rpcInternalError("Failed to create new block "+ + // "template: not enough voters on parent and no "+ + // "suitable cached template", "") + // } + // template = blkTemplate + // msgBlock = template.Block + // targetDifficulty = fmt.Sprintf("%064x", + // blockchain.CompactToBig(msgBlock.Header.Bits)) + + // // Find the minimum allowed timestamp for the block based on the + // // median timestamp of the last several blocks per the chain + // // consensus rules. + // chainState := &s.server.blockManager.chainState + // minTimestamp, err := minimumMedianTime(chainState) + // if err != nil { + // context := "Failed to get minimum median time" + // return rpcInternalError(err.Error(), context) + // } + + // // Update work state to ensure another block template isn't + // // generated until needed. + // state.template = deepCopyBlockTemplate(template) + // state.lastGenerated = time.Now() + // state.lastTxUpdate = lastTxUpdate + // state.prevHash = latestHash + // state.minTimestamp = minTimestamp + + // rpcsLog.Debugf("Generated block template (timestamp %v, "+ + // "target %s, merkle root %s)", + // msgBlock.Header.Timestamp, targetDifficulty, + // msgBlock.Header.MerkleRoot) + + // // Notify any clients that are long polling about the new + // // template. + // state.notifyLongPollers(latestHash, lastTxUpdate) + // } else { + // // At this point, there is a saved block template and another + // // request for a template was made, but either the available + // // transactions haven't change or it hasn't been long enough to + // // trigger a new block template to be generated. So, update the + // // existing block template. + + // // When the caller requires a full coinbase as opposed to only + // // the pertinent details needed to create their own coinbase, + // // add a payment address to the output of the coinbase of the + // // template if it doesn't already have one. Since this requires + // // mining addresses to be specified via the config, an error is + // // returned if none have been specified. + // if !useCoinbaseValue && !template.ValidPayAddress { + // // Choose a payment address at random. + // payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + + // // Update the block coinbase output of the template to + // // pay to the randomly selected payment address. + // pkScript, err := txscript.PayToAddrScript(payToAddr) + // if err != nil { + // context := "Failed to create pay-to-addr script" + // return rpcInternalError(err.Error(), context) + // } + // template.Block.Transactions[0].TxOut[0].PkScript = pkScript + // template.ValidPayAddress = true + + // // Update the merkle root. + // block := dcrutil.NewBlock(template.Block) + // merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + // template.Block.Header.MerkleRoot = *merkles[len(merkles)-1] + // } + + // // Set locals for convenience. + // msgBlock = template.Block + // targetDifficulty = fmt.Sprintf("%064x", + // blockchain.CompactToBig(msgBlock.Header.Bits)) + + // // 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. + // err := UpdateBlockTime(msgBlock, s.server.blockManager) + // if err != nil { + // context := "Failed to update timestamp" + // return rpcInternalError(err.Error(), context) + // } + // msgBlock.Header.Nonce = 0 + + // rpcsLog.Debugf("Updated block template (timestamp %v, "+ + // "target %s)", msgBlock.Header.Timestamp, + // targetDifficulty) + // } return nil } @@ -4037,181 +4037,183 @@ func pruneOldBlockTemplates(s *rpcServer, bestHeight int64) { // // This function MUST be called with the RPC workstate locked. func handleGetWorkRequest(s *rpcServer) (interface{}, error) { - state := s.workState - - // Generate a new block template when the current best block has - // changed or the transactions in the memory pool have been updated and - // it has been at least one minute since the last template was - // generated. - lastTxUpdate := s.server.txMemPool.LastUpdated() - latestHash, latestHeight := s.server.blockManager.chainState.Best() - msgBlock := state.msgBlock - - // The current code pulls down a new template every second, however - // with a large mempool this will be pretty excruciating sometimes. It - // should examine whether or not a new template needs to be created - // based on the votes present every second or so, and then, if needed, - // generate a new block template. TODO cj - if msgBlock == nil || state.prevHash == nil || - !state.prevHash.IsEqual(latestHash) || - (state.lastTxUpdate != lastTxUpdate && - time.Now().After(state.lastGenerated.Add(time.Second))) { - // Reset the extra nonce and clear all expired cached template - // variations if the best block changed. - if state.prevHash != nil && !state.prevHash.IsEqual(latestHash) { - state.extraNonce = 0 - pruneOldBlockTemplates(s, latestHeight) - } - - // Reset the previous best hash the block template was - // generated against so any errors below cause the next - // invocation to try again. - state.prevHash = nil - - // Choose a payment address at random. - payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - - template, err := NewBlockTemplate(s.policy, s.server, payToAddr) - if err != nil { - context := "Failed to create new block template" - return nil, rpcInternalError(err.Error(), context) - } - if template == nil { - // This happens if the template is returned nil because - // there are not enough voters on HEAD and there is - // currently an unsuitable parent cached template to - // try building off of. - context := "Failed to create new block template: not " + - "enough voters and failed to find a suitable " + - "parent template to build from" - return nil, rpcInternalError("internal error", context) - } - templateCopy := deepCopyBlockTemplate(template) - msgBlock = templateCopy.Block - - // Update work state to ensure another block template isn't - // generated until needed. - state.msgBlock = msgBlock - state.lastGenerated = time.Now() - state.lastTxUpdate = lastTxUpdate - state.prevHash = latestHash - - rpcsLog.Debugf("Generated block template (timestamp %v, extra "+ - "nonce %d, target %064x, merkle root %s)", - msgBlock.Header.Timestamp, state.extraNonce, - blockchain.CompactToBig(msgBlock.Header.Bits), - msgBlock.Header.MerkleRoot) - } else { - if msgBlock == nil { - context := "Failed to create new block template, " + - "no previous state" - return nil, rpcInternalError("internal error", context) - } - - // At this point, there is a saved block template and a new - // request for work was made, but either the available - // transactions haven't change or it hasn't been long enough to - // trigger a new block template to be generated. So, update the - // existing block template and track the variations so each - // variation can be regenerated if a caller finds an answer and - // makes a submission against it. - templateCopy := deepCopyBlockTemplate(&BlockTemplate{ - Block: msgBlock, - }) - msgBlock = templateCopy.Block - - // 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. - err := UpdateBlockTime(msgBlock, s.server.blockManager) - if err != nil { - return nil, rpcInternalError(err.Error(), - "Failed to update block time") - } + // state := s.workState + + // // Generate a new block template when the current best block has + // // changed or the transactions in the memory pool have been updated and + // // it has been at least one minute since the last template was + // // generated. + // lastTxUpdate := s.server.txMemPool.LastUpdated() + // latestHash, latestHeight := s.server.blockManager.chainState.Best() + // msgBlock := state.msgBlock + + // // The current code pulls down a new template every second, however + // // with a large mempool this will be pretty excruciating sometimes. It + // // should examine whether or not a new template needs to be created + // // based on the votes present every second or so, and then, if needed, + // // generate a new block template. TODO cj + // if msgBlock == nil || state.prevHash == nil || + // !state.prevHash.IsEqual(latestHash) || + // (state.lastTxUpdate != lastTxUpdate && + // time.Now().After(state.lastGenerated.Add(time.Second))) { + // // Reset the extra nonce and clear all expired cached template + // // variations if the best block changed. + // if state.prevHash != nil && !state.prevHash.IsEqual(latestHash) { + // state.extraNonce = 0 + // pruneOldBlockTemplates(s, latestHeight) + // } + + // // Reset the previous best hash the block template was + // // generated against so any errors below cause the next + // // invocation to try again. + // state.prevHash = nil + + // // Choose a payment address at random. + // payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + + // template, err := NewBlockTemplate(s.policy, s.server, payToAddr) + // if err != nil { + // context := "Failed to create new block template" + // return nil, rpcInternalError(err.Error(), context) + // } + // if template == nil { + // // This happens if the template is returned nil because + // // there are not enough voters on HEAD and there is + // // currently an unsuitable parent cached template to + // // try building off of. + // context := "Failed to create new block template: not " + + // "enough voters and failed to find a suitable " + + // "parent template to build from" + // return nil, rpcInternalError("internal error", context) + // } + // templateCopy := deepCopyBlockTemplate(template) + // msgBlock = templateCopy.Block + + // // Update work state to ensure another block template isn't + // // generated until needed. + // state.msgBlock = msgBlock + // state.lastGenerated = time.Now() + // state.lastTxUpdate = lastTxUpdate + // state.prevHash = latestHash + + // rpcsLog.Debugf("Generated block template (timestamp %v, extra "+ + // "nonce %d, target %064x, merkle root %s)", + // msgBlock.Header.Timestamp, state.extraNonce, + // blockchain.CompactToBig(msgBlock.Header.Bits), + // msgBlock.Header.MerkleRoot) + // } else { + // if msgBlock == nil { + // context := "Failed to create new block template, " + + // "no previous state" + // return nil, rpcInternalError("internal error", context) + // } + + // // At this point, there is a saved block template and a new + // // request for work was made, but either the available + // // transactions haven't change or it hasn't been long enough to + // // trigger a new block template to be generated. So, update the + // // existing block template and track the variations so each + // // variation can be regenerated if a caller finds an answer and + // // makes a submission against it. + // templateCopy := deepCopyBlockTemplate(&BlockTemplate{ + // Block: msgBlock, + // }) + // msgBlock = templateCopy.Block + + // // 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. + // err := UpdateBlockTime(msgBlock, s.server.blockManager) + // if err != nil { + // return nil, rpcInternalError(err.Error(), + // "Failed to update block time") + // } + + // if templateCopy.Height > 1 { + // // 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. + // en := extractCoinbaseExtraNonce(msgBlock) + 1 + // state.extraNonce++ + // err := UpdateExtraNonce(msgBlock, latestHeight+1, en) + // if err != nil { + // errStr := fmt.Sprintf("Failed to update extra nonce: "+ + // "%v", err) + // return nil, rpcInternalError(errStr, "") + // } + // } + + // rpcsLog.Debugf("Updated block template (timestamp %v, extra "+ + // "nonce %d, target %064x, merkle root %s)", + // msgBlock.Header.Timestamp, + // state.extraNonce, + // blockchain.CompactToBig(msgBlock.Header.Bits), + // msgBlock.Header.MerkleRoot) + // } + + // // In order to efficiently store the variations of block templates that + // // have been provided to callers, save a pointer to the block as well + // // as the modified signature script keyed by the merkle root. This + // // information, along with the data that is included in a work + // // submission, is used to rebuild the block before checking the + // // submitted solution. + // coinbaseTx := msgBlock.Transactions[0] + + // // Create the new merkleRootPair key which is MerkleRoot + StakeRoot + // var merkleRootPair [merkleRootPairSize]byte + // copy(merkleRootPair[:chainhash.HashSize], msgBlock.Header.MerkleRoot[:]) + // copy(merkleRootPair[chainhash.HashSize:], msgBlock.Header.StakeRoot[:]) + + // if msgBlock.Header.Height > 1 { + // s.templatePool[merkleRootPair] = &workStateBlockInfo{ + // msgBlock: msgBlock, + // pkScript: coinbaseTx.TxOut[1].PkScript, + // } + // } else { + // s.templatePool[merkleRootPair] = &workStateBlockInfo{ + // msgBlock: msgBlock, + // } + // } + + // // Serialize the block header into a buffer large enough to hold the + // // the block header and the internal blake256 padding that is added and + // // retuned as part of the data below. For reference: + // // data[116] --> nBits + // // data[136] --> Timestamp + // // data[140] --> nonce + // data := make([]byte, 0, getworkDataLen) + // buf := bytes.NewBuffer(data) + // err := msgBlock.Header.Serialize(buf) + // if err != nil { + // errStr := fmt.Sprintf("Failed to serialize data: %v", err) + // return nil, rpcInternalError(errStr, "") + // } + + // // Expand the data slice to include the full data buffer and apply the + // // internal blake256 padding. This makes the data ready for callers to + // // make use of only the final chunk along with the midstate for the + // // rest. + // data = data[:getworkDataLen] + // copy(data[wire.MaxBlockHeaderPayload:], blake256Pad) + + // // The final result reverses each of the fields to little endian. In + // // particular, the data, hash1, and midstate fields are treated as + // // arrays of uint32s (per the internal sha256 hashing state) which are + // // in big endian, and thus each 4 bytes is byte swapped. The target is + // // also in big endian, but it is treated as a uint256 and byte swapped + // // to little endian accordingly. + // // + // // The fact the fields are reversed in this way is rather odd and likey + // // an artifact of some legacy internal state in the reference + // // implementation, but it is required for compatibility. + // target := bigToLEUint256(blockchain.CompactToBig(msgBlock.Header.Bits)) + // reply := &dcrjson.GetWorkResult{ + // Data: hex.EncodeToString(data), + // Target: hex.EncodeToString(target[:]), + // } + //return reply, nil - if templateCopy.Height > 1 { - // 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. - en := extractCoinbaseExtraNonce(msgBlock) + 1 - state.extraNonce++ - err := UpdateExtraNonce(msgBlock, latestHeight+1, en) - if err != nil { - errStr := fmt.Sprintf("Failed to update extra nonce: "+ - "%v", err) - return nil, rpcInternalError(errStr, "") - } - } - - rpcsLog.Debugf("Updated block template (timestamp %v, extra "+ - "nonce %d, target %064x, merkle root %s)", - msgBlock.Header.Timestamp, - state.extraNonce, - blockchain.CompactToBig(msgBlock.Header.Bits), - msgBlock.Header.MerkleRoot) - } - - // In order to efficiently store the variations of block templates that - // have been provided to callers, save a pointer to the block as well - // as the modified signature script keyed by the merkle root. This - // information, along with the data that is included in a work - // submission, is used to rebuild the block before checking the - // submitted solution. - coinbaseTx := msgBlock.Transactions[0] - - // Create the new merkleRootPair key which is MerkleRoot + StakeRoot - var merkleRootPair [merkleRootPairSize]byte - copy(merkleRootPair[:chainhash.HashSize], msgBlock.Header.MerkleRoot[:]) - copy(merkleRootPair[chainhash.HashSize:], msgBlock.Header.StakeRoot[:]) - - if msgBlock.Header.Height > 1 { - s.templatePool[merkleRootPair] = &workStateBlockInfo{ - msgBlock: msgBlock, - pkScript: coinbaseTx.TxOut[1].PkScript, - } - } else { - s.templatePool[merkleRootPair] = &workStateBlockInfo{ - msgBlock: msgBlock, - } - } - - // Serialize the block header into a buffer large enough to hold the - // the block header and the internal blake256 padding that is added and - // retuned as part of the data below. For reference: - // data[116] --> nBits - // data[136] --> Timestamp - // data[140] --> nonce - data := make([]byte, 0, getworkDataLen) - buf := bytes.NewBuffer(data) - err := msgBlock.Header.Serialize(buf) - if err != nil { - errStr := fmt.Sprintf("Failed to serialize data: %v", err) - return nil, rpcInternalError(errStr, "") - } - - // Expand the data slice to include the full data buffer and apply the - // internal blake256 padding. This makes the data ready for callers to - // make use of only the final chunk along with the midstate for the - // rest. - data = data[:getworkDataLen] - copy(data[wire.MaxBlockHeaderPayload:], blake256Pad) - - // The final result reverses each of the fields to little endian. In - // particular, the data, hash1, and midstate fields are treated as - // arrays of uint32s (per the internal sha256 hashing state) which are - // in big endian, and thus each 4 bytes is byte swapped. The target is - // also in big endian, but it is treated as a uint256 and byte swapped - // to little endian accordingly. - // - // The fact the fields are reversed in this way is rather odd and likey - // an artifact of some legacy internal state in the reference - // implementation, but it is required for compatibility. - target := bigToLEUint256(blockchain.CompactToBig(msgBlock.Header.Bits)) - reply := &dcrjson.GetWorkResult{ - Data: hex.EncodeToString(data), - Target: hex.EncodeToString(target[:]), - } - return reply, nil + return nil, nil } // handleGetWorkSubmission is a helper for handleGetWork which deals with diff --git a/rpcserver_test.go b/rpcserver_test.go index a0496daf28..e748bb5110 100644 --- a/rpcserver_test.go +++ b/rpcserver_test.go @@ -39,7 +39,7 @@ func testGetBestBlock(r *rpctest.Harness, t *testing.T) { // Hash should be the same as the newly submitted block. if !bytes.Equal(bestHash[:], generatedBlockHashes[0][:]) { t.Fatalf("Block hashes do not match. Returned hash %v, wanted "+ - "hash %v", bestHash, generatedBlockHashes[0][:]) + "hash %v", bestHash, generatedBlockHashes[0]) } // Block height should now reflect newest height. @@ -91,7 +91,7 @@ func testGetBlockHash(r *rpctest.Harness, t *testing.T) { // Block hashes should match newly created block. if !bytes.Equal(generatedBlockHashes[0][:], blockHash[:]) { t.Fatalf("Block hashes do not match. Returned hash %v, wanted "+ - "hash %v", blockHash, generatedBlockHashes[0][:]) + "hash %v", blockHash, generatedBlockHashes[0]) } } diff --git a/server.go b/server.go index 137193571d..deaafe4c52 100644 --- a/server.go +++ b/server.go @@ -2459,7 +2459,14 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param if len(indexes) > 0 { indexManager = indexers.NewManager(db, indexes, chainParams) } - bm, err := newBlockManager(&s, indexManager, interrupt) + + // A config with mining address(es) connotes a mining node. + var isMining bool + if len(cfg.miningAddrs) > 0 { + isMining = true + } + + bm, err := newBlockManager(&s, indexManager, isMining, interrupt) if err != nil { return nil, err } @@ -2497,6 +2504,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime }, AddrIndex: s.addrIndex, ExistsAddrIndex: s.existsAddrIndex, + RegenTemplate: func() { bm.RegenTemplate() }, + NonVoteTx: func() { bm.ReceiveNonVoteTx() }, } s.txMemPool = mempool.New(&txC)