// solveBlock attempts to find a nonce which makes the passed block header hash // to a value less than the target difficulty. When a successful solution is // found true is returned and the nonce field of the passed header is updated // with the solution. False is returned if no solution exists. func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool { // sbResult is used by the solver goroutines to send results. type sbResult struct { found bool nonce uint32 } // solver accepts a block header and a nonce range to test. It is // intended to be run as a goroutine. quit := make(chan bool) results := make(chan sbResult) solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) { // We need to modify the nonce field of the header, so make sure // we work with a copy of the original header. for i := startNonce; i >= startNonce && i <= stopNonce; i++ { select { case <-quit: return default: hdr.Nonce = i hash := hdr.BlockHash() if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 { results <- sbResult{true, i} return } } } results <- sbResult{false, 0} } startNonce := uint32(0) stopNonce := uint32(math.MaxUint32) numCores := uint32(runtime.NumCPU()) noncesPerCore := (stopNonce - startNonce) / numCores for i := uint32(0); i < numCores; i++ { rangeStart := startNonce + (noncesPerCore * i) rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1 if i == numCores-1 { rangeStop = stopNonce } go solver(*header, rangeStart, rangeStop) } for i := uint32(0); i < numCores; i++ { result := <-results if result.found { close(quit) header.Nonce = result.nonce return true } } return false }
// solveBlock attempts to find some combination of a nonce, extra nonce, and // current timestamp which makes the passed block hash to a value less than the // target difficulty. The timestamp is updated periodically and the passed // block is modified with all tweaks during this process. This means that // when the function returns true, the block is ready for submission. // // 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, blockHeight int32, ticker *time.Ticker, quit chan struct{}) bool { // Choose a random extra nonce offset for this block template and // worker. enOffset, err := wire.RandomUint64() if err != nil { log.Errorf("Unexpected error while generating random "+ "extra nonce offset: %v", err) enOffset = 0 } // Create some convenience variables. header := &msgBlock.Header targetDifficulty := blockchain.CompactToBig(header.Bits) // Initial state. lastGenerated := time.Now() lastTxUpdate := m.g.TxSource().LastUpdated() hashesCompleted := uint64(0) // Note that the entire extra nonce range is iterated and the offset is // added relying on the fact that overflow will wrap around 0 as // provided by the Go spec. for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { // 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. 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 // conditions along with updates to the speed monitor. for i := uint32(0); i <= maxNonce; i++ { select { case <-quit: return false case <-ticker.C: m.updateHashes <- hashesCompleted hashesCompleted = 0 // The current block is stale if the best block // has changed. best := m.g.BestSnapshot() if !header.PrevBlock.IsEqual(best.Hash) { return false } // The current block is stale if the memory pool // has been updated since the block template was // generated and it has been at least one // minute. if lastTxUpdate != m.g.TxSource().LastUpdated() && time.Now().After(lastGenerated.Add(time.Minute)) { return false } m.g.UpdateBlockTime(msgBlock) default: // Non-blocking select to fall through } // Update the nonce and hash the block header. Each // hash is actually a double sha256 (two hashes), so // increment the number of hashes completed for each // attempt accordingly. header.Nonce = i hash := header.BlockHash() hashesCompleted += 2 // The block is solved when the new block hash is less // than the target difficulty. Yay! if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 { m.updateHashes <- hashesCompleted return true } } } return false }