github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/consensus/ethash/sealer.go (about) 1 // Copyright 2017 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Spectrum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethash 18 19 import ( 20 crand "crypto/rand" 21 "math" 22 "math/big" 23 "math/rand" 24 "runtime" 25 "sync" 26 27 "github.com/SmartMeshFoundation/Spectrum/common" 28 "github.com/SmartMeshFoundation/Spectrum/consensus" 29 "github.com/SmartMeshFoundation/Spectrum/core/types" 30 "github.com/SmartMeshFoundation/Spectrum/log" 31 ) 32 33 // Seal implements consensus.Engine, attempting to find a nonce that satisfies 34 // the block's difficulty requirements. 35 func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) { 36 // If we're running a fake PoW, simply return a 0 nonce immediately 37 if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { 38 header := block.Header() 39 header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{} 40 return block.WithSeal(header), nil 41 } 42 // If we're running a shared PoW, delegate sealing to it 43 if ethash.shared != nil { 44 return ethash.shared.Seal(chain, block, stop) 45 } 46 // Create a runner and the multiple search threads it directs 47 abort := make(chan struct{}) 48 found := make(chan *types.Block) 49 50 ethash.lock.Lock() 51 threads := ethash.threads 52 if ethash.rand == nil { 53 seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) 54 if err != nil { 55 ethash.lock.Unlock() 56 return nil, err 57 } 58 ethash.rand = rand.New(rand.NewSource(seed.Int64())) 59 } 60 ethash.lock.Unlock() 61 if threads == 0 { 62 threads = runtime.NumCPU() 63 } 64 if threads < 0 { 65 threads = 0 // Allows disabling local mining without extra logic around local/remote 66 } 67 var pend sync.WaitGroup 68 for i := 0; i < threads; i++ { 69 pend.Add(1) 70 go func(id int, nonce uint64) { 71 defer pend.Done() 72 ethash.mine(block, id, nonce, abort, found) 73 }(i, uint64(ethash.rand.Int63())) 74 } 75 // Wait until sealing is terminated or a nonce is found 76 var result *types.Block 77 select { 78 case <-stop: 79 // Outside abort, stop all miner threads 80 close(abort) 81 case result = <-found: 82 // One of the threads found a block, abort all others 83 close(abort) 84 case <-ethash.update: 85 // Thread count was changed on user request, restart 86 close(abort) 87 pend.Wait() 88 return ethash.Seal(chain, block, stop) 89 } 90 // Wait for all miners to terminate and return the block 91 pend.Wait() 92 return result, nil 93 } 94 95 // mine is the actual proof-of-work miner that searches for a nonce starting from 96 // seed that results in correct final block difficulty. 97 func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) { 98 // Extract some data from the header 99 var ( 100 header = block.Header() 101 hash = header.HashNoNonce().Bytes() 102 target = new(big.Int).Div(maxUint256, header.Difficulty) 103 104 number = header.Number.Uint64() 105 dataset = ethash.dataset(number) 106 ) 107 // Start generating random nonces until we abort or find a good one 108 var ( 109 attempts = int64(0) 110 nonce = seed 111 ) 112 logger := log.New("miner", id) 113 logger.Trace("Started ethash search for new nonces", "seed", seed) 114 for { 115 select { 116 case <-abort: 117 // Mining terminated, update stats and abort 118 logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed) 119 ethash.hashrate.Mark(attempts) 120 return 121 122 default: 123 // We don't have to update hash rate on every nonce, so update after after 2^X nonces 124 attempts++ 125 if (attempts % (1 << 15)) == 0 { 126 ethash.hashrate.Mark(attempts) 127 attempts = 0 128 } 129 // Compute the PoW value of this nonce 130 digest, result := hashimotoFull(dataset, hash, nonce) 131 if new(big.Int).SetBytes(result).Cmp(target) <= 0 { 132 // Correct nonce found, create a new header with it 133 header = types.CopyHeader(header) 134 header.Nonce = types.EncodeNonce(nonce) 135 header.MixDigest = common.BytesToHash(digest) 136 137 // Seal and return a block (if still needed) 138 select { 139 case found <- block.WithSeal(header): 140 logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce) 141 case <-abort: 142 logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce) 143 } 144 return 145 } 146 nonce++ 147 } 148 } 149 }