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