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