github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/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/ethereumproject/ethash" 27 "github.com/ethereumproject/go-ethereum/common" 28 "github.com/ethereumproject/go-ethereum/logger" 29 "github.com/ethereumproject/go-ethereum/logger/glog" 30 ) 31 32 type hashrate struct { 33 ping time.Time 34 rate uint64 35 } 36 37 type RemoteAgent struct { 38 mu sync.Mutex 39 40 quit chan struct{} 41 workCh chan *Work 42 returnCh chan<- *Result 43 44 currentWork *Work 45 work map[common.Hash]*Work 46 47 hashrateMu sync.RWMutex 48 hashrate map[common.Hash]hashrate 49 50 running int32 // running indicates whether the agent is active. Call atomically 51 } 52 53 func NewRemoteAgent() *RemoteAgent { 54 return &RemoteAgent{ 55 work: make(map[common.Hash]*Work), 56 hashrate: make(map[common.Hash]hashrate), 57 } 58 } 59 60 func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) { 61 a.hashrateMu.Lock() 62 defer a.hashrateMu.Unlock() 63 64 a.hashrate[id] = hashrate{time.Now(), rate} 65 } 66 67 func (a *RemoteAgent) Work() chan<- *Work { 68 return a.workCh 69 } 70 71 func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { 72 a.returnCh = returnCh 73 } 74 75 func (a *RemoteAgent) Start() { 76 if !atomic.CompareAndSwapInt32(&a.running, 0, 1) { 77 return 78 } 79 80 a.quit = make(chan struct{}) 81 a.workCh = make(chan *Work, 1) 82 go a.maintainLoop() 83 } 84 85 func (a *RemoteAgent) Stop() { 86 if !atomic.CompareAndSwapInt32(&a.running, 1, 0) { 87 return 88 } 89 90 close(a.quit) 91 close(a.workCh) 92 } 93 94 // GetHashRate returns the accumulated hashrate of all identifier combined 95 func (a *RemoteAgent) GetHashRate() (tot int64) { 96 a.hashrateMu.RLock() 97 defer a.hashrateMu.RUnlock() 98 99 // this could overflow 100 for _, hashrate := range a.hashrate { 101 tot += int64(hashrate.rate) 102 } 103 return 104 } 105 106 func (a *RemoteAgent) GetWork() ([3]string, error) { 107 a.mu.Lock() 108 defer a.mu.Unlock() 109 110 var res [3]string 111 112 if a.currentWork != nil { 113 block := a.currentWork.Block 114 115 res[0] = block.HashNoNonce().Hex() 116 seedHash, _ := ethash.GetSeedHash(block.NumberU64()) 117 res[1] = common.BytesToHash(seedHash).Hex() 118 // Calculate the "target" to be returned to the external miner 119 n := big.NewInt(1) 120 n.Lsh(n, 255) 121 n.Div(n, block.Difficulty()) 122 n.Lsh(n, 1) 123 res[2] = common.BytesToHash(n.Bytes()).Hex() 124 125 a.work[block.HashNoNonce()] = a.currentWork 126 return res, nil 127 } 128 return res, errors.New("No work available yet, don't panic.") 129 } 130 131 // Returns true or false, but does not indicate if the PoW was correct 132 func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, hash common.Hash) (exists bool) { 133 a.mu.Lock() 134 defer a.mu.Unlock() 135 defer func() { 136 if logger.MlogEnabled() { 137 mlogMinerSubmitWork.AssignDetails( 138 nonce, 139 mixDigest.Hex(), 140 hash.Hex(), 141 exists, 142 ).Send(mlogMiner) 143 } 144 }() 145 146 // Make sure the work submitted is present 147 if a.work[hash] != nil { 148 block := a.work[hash].Block.WithMiningResult(nonce, mixDigest) 149 a.returnCh <- &Result{a.work[hash], block} 150 151 delete(a.work, hash) 152 153 exists = true 154 return 155 } else { 156 glog.V(logger.Info).Infof("Work was submitted for %x but no pending work found\n", hash) 157 } 158 159 exists = false 160 return 161 } 162 163 func (a *RemoteAgent) maintainLoop() { 164 ticker := time.Tick(5 * time.Second) 165 166 out: 167 for { 168 select { 169 case <-a.quit: 170 break out 171 case work := <-a.workCh: 172 a.mu.Lock() 173 a.currentWork = work 174 a.mu.Unlock() 175 case <-ticker: 176 // cleanup 177 a.mu.Lock() 178 for hash, work := range a.work { 179 if time.Since(work.createdAt) > 7*(12*time.Second) { 180 delete(a.work, hash) 181 } 182 } 183 a.mu.Unlock() 184 185 a.hashrateMu.Lock() 186 for id, hashrate := range a.hashrate { 187 if time.Since(hashrate.ping) > 10*time.Second { 188 delete(a.hashrate, id) 189 } 190 } 191 a.hashrateMu.Unlock() 192 } 193 } 194 }