github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/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 "fmt" 22 "math/big" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/pow" 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 pow pow.PoW 46 currentWork *Work 47 work map[common.Hash]*Work 48 49 hashrateMu sync.RWMutex 50 hashrate map[common.Hash]hashrate 51 52 running int32 // running indicates whether the agent is active. Call atomically 53 } 54 55 func NewRemoteAgent(pow pow.PoW) *RemoteAgent { 56 return &RemoteAgent{ 57 pow: pow, 58 work: make(map[common.Hash]*Work), 59 hashrate: make(map[common.Hash]hashrate), 60 } 61 } 62 63 func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) { 64 a.hashrateMu.Lock() 65 defer a.hashrateMu.Unlock() 66 67 a.hashrate[id] = hashrate{time.Now(), rate} 68 } 69 70 func (a *RemoteAgent) Work() chan<- *Work { 71 return a.workCh 72 } 73 74 func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { 75 a.returnCh = returnCh 76 } 77 78 func (a *RemoteAgent) Start() { 79 if !atomic.CompareAndSwapInt32(&a.running, 0, 1) { 80 return 81 } 82 a.quitCh = make(chan struct{}) 83 a.workCh = make(chan *Work, 1) 84 go a.loop(a.workCh, a.quitCh) 85 } 86 87 func (a *RemoteAgent) Stop() { 88 if !atomic.CompareAndSwapInt32(&a.running, 1, 0) { 89 return 90 } 91 close(a.quitCh) 92 close(a.workCh) 93 } 94 95 // GetHashRate returns the accumulated hashrate of all identifier combined 96 func (a *RemoteAgent) GetHashRate() (tot int64) { 97 a.hashrateMu.RLock() 98 defer a.hashrateMu.RUnlock() 99 100 // this could overflow 101 for _, hashrate := range a.hashrate { 102 tot += int64(hashrate.rate) 103 } 104 return 105 } 106 107 func (a *RemoteAgent) GetWork() ([3]string, error) { 108 a.mu.Lock() 109 defer a.mu.Unlock() 110 111 var res [3]string 112 113 if a.currentWork != nil { 114 block := a.currentWork.Block 115 116 res[0] = block.HashNoNonce().Hex() 117 seedHash := pow.EthashSeedHash(block.NumberU64()) 118 res[1] = common.BytesToHash(seedHash).Hex() 119 // Calculate the "target" to be returned to the external miner 120 n := big.NewInt(1) 121 n.Lsh(n, 255) 122 n.Div(n, block.Difficulty()) 123 n.Lsh(n, 1) 124 res[2] = common.BytesToHash(n.Bytes()).Hex() 125 126 a.work[block.HashNoNonce()] = a.currentWork 127 return res, nil 128 } 129 return res, errors.New("No work available yet, don't panic.") 130 } 131 132 // SubmitWork tries to inject a PoW solution tinto the remote agent, returning 133 // whether the solution was acceted or not (not can be both a bad PoW as well as 134 // any other error, like no work pending). 135 func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { 136 a.mu.Lock() 137 defer a.mu.Unlock() 138 139 // Make sure the work submitted is present 140 work := a.work[hash] 141 if work == nil { 142 log.Info(fmt.Sprintf("Work was submitted for %x but no pending work found", hash)) 143 return false 144 } 145 // Make sure the PoW solutions is indeed valid 146 block := work.Block.WithMiningResult(nonce, mixDigest) 147 if err := a.pow.Verify(block); err != nil { 148 log.Warn(fmt.Sprintf("Invalid PoW submitted for %x: %v", hash, err)) 149 return false 150 } 151 // Solutions seems to be valid, return to the miner and notify acceptance 152 a.returnCh <- &Result{work, block} 153 delete(a.work, hash) 154 155 return true 156 } 157 158 // loop monitors mining events on the work and quit channels, updating the internal 159 // state of the rmeote miner until a termination is requested. 160 // 161 // Note, the reason the work and quit channels are passed as parameters is because 162 // RemoteAgent.Start() constantly recreates these channels, so the loop code cannot 163 // assume data stability in these member fields. 164 func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) { 165 ticker := time.Tick(5 * time.Second) 166 167 for { 168 select { 169 case <-quitCh: 170 return 171 case work := <-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 }