github.com/ylsgit/go-ethereum@v1.6.5/miner/remote_agent.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package miner 18 19 import ( 20 "errors" 21 "math/big" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/consensus" 28 "github.com/ethereum/go-ethereum/consensus/ethash" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/log" 31 ) 32 33 type hashrate struct { 34 ping time.Time 35 rate uint64 36 } 37 38 type RemoteAgent struct { 39 mu sync.Mutex 40 41 quitCh chan struct{} 42 workCh chan *Work 43 returnCh chan<- *Result 44 45 chain consensus.ChainReader 46 engine consensus.Engine 47 currentWork *Work 48 work map[common.Hash]*Work 49 50 hashrateMu sync.RWMutex 51 hashrate map[common.Hash]hashrate 52 53 running int32 // running indicates whether the agent is active. Call atomically 54 } 55 56 func NewRemoteAgent(chain consensus.ChainReader, engine consensus.Engine) *RemoteAgent { 57 return &RemoteAgent{ 58 chain: chain, 59 engine: engine, 60 work: make(map[common.Hash]*Work), 61 hashrate: make(map[common.Hash]hashrate), 62 } 63 } 64 65 func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) { 66 a.hashrateMu.Lock() 67 defer a.hashrateMu.Unlock() 68 69 a.hashrate[id] = hashrate{time.Now(), rate} 70 } 71 72 func (a *RemoteAgent) Work() chan<- *Work { 73 return a.workCh 74 } 75 76 func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { 77 a.returnCh = returnCh 78 } 79 80 func (a *RemoteAgent) Start() { 81 if !atomic.CompareAndSwapInt32(&a.running, 0, 1) { 82 return 83 } 84 a.quitCh = make(chan struct{}) 85 a.workCh = make(chan *Work, 1) 86 go a.loop(a.workCh, a.quitCh) 87 } 88 89 func (a *RemoteAgent) Stop() { 90 if !atomic.CompareAndSwapInt32(&a.running, 1, 0) { 91 return 92 } 93 close(a.quitCh) 94 close(a.workCh) 95 } 96 97 // GetHashRate returns the accumulated hashrate of all identifier combined 98 func (a *RemoteAgent) GetHashRate() (tot int64) { 99 a.hashrateMu.RLock() 100 defer a.hashrateMu.RUnlock() 101 102 // this could overflow 103 for _, hashrate := range a.hashrate { 104 tot += int64(hashrate.rate) 105 } 106 return 107 } 108 109 func (a *RemoteAgent) GetWork() ([3]string, error) { 110 a.mu.Lock() 111 defer a.mu.Unlock() 112 113 var res [3]string 114 115 if a.currentWork != nil { 116 block := a.currentWork.Block 117 118 res[0] = block.HashNoNonce().Hex() 119 seedHash := ethash.SeedHash(block.NumberU64()) 120 res[1] = common.BytesToHash(seedHash).Hex() 121 // Calculate the "target" to be returned to the external miner 122 n := big.NewInt(1) 123 n.Lsh(n, 255) 124 n.Div(n, block.Difficulty()) 125 n.Lsh(n, 1) 126 res[2] = common.BytesToHash(n.Bytes()).Hex() 127 128 a.work[block.HashNoNonce()] = a.currentWork 129 return res, nil 130 } 131 return res, errors.New("No work available yet, don't panic.") 132 } 133 134 // SubmitWork tries to inject a pow solution into the remote agent, returning 135 // whether the solution was accepted or not (not can be both a bad pow as well as 136 // any other error, like no work pending). 137 func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { 138 a.mu.Lock() 139 defer a.mu.Unlock() 140 141 // Make sure the work submitted is present 142 work := a.work[hash] 143 if work == nil { 144 log.Info("Work submitted but none pending", "hash", hash) 145 return false 146 } 147 // Make sure the Engine solutions is indeed valid 148 result := work.Block.Header() 149 result.Nonce = nonce 150 result.MixDigest = mixDigest 151 152 if err := a.engine.VerifySeal(a.chain, result); err != nil { 153 log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err) 154 return false 155 } 156 block := work.Block.WithSeal(result) 157 158 // Solutions seems to be valid, return to the miner and notify acceptance 159 a.returnCh <- &Result{work, block} 160 delete(a.work, hash) 161 162 return true 163 } 164 165 // loop monitors mining events on the work and quit channels, updating the internal 166 // state of the rmeote miner until a termination is requested. 167 // 168 // Note, the reason the work and quit channels are passed as parameters is because 169 // RemoteAgent.Start() constantly recreates these channels, so the loop code cannot 170 // assume data stability in these member fields. 171 func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) { 172 ticker := time.Tick(5 * time.Second) 173 174 for { 175 select { 176 case <-quitCh: 177 return 178 case work := <-workCh: 179 a.mu.Lock() 180 a.currentWork = work 181 a.mu.Unlock() 182 case <-ticker: 183 // cleanup 184 a.mu.Lock() 185 for hash, work := range a.work { 186 if time.Since(work.createdAt) > 7*(12*time.Second) { 187 delete(a.work, hash) 188 } 189 } 190 a.mu.Unlock() 191 192 a.hashrateMu.Lock() 193 for id, hashrate := range a.hashrate { 194 if time.Since(hashrate.ping) > 10*time.Second { 195 delete(a.hashrate, id) 196 } 197 } 198 a.hashrateMu.Unlock() 199 } 200 } 201 }