gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/opt/miner/remote_agent.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain 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 "gitlab.com/aquachain/aquachain/common" 28 "gitlab.com/aquachain/aquachain/common/log" 29 "gitlab.com/aquachain/aquachain/consensus" 30 "gitlab.com/aquachain/aquachain/consensus/aquahash" 31 "gitlab.com/aquachain/aquachain/core" 32 "gitlab.com/aquachain/aquachain/core/types" 33 "gitlab.com/aquachain/aquachain/rlp" 34 ) 35 36 type hashrate struct { 37 ping time.Time 38 rate uint64 39 } 40 41 type RemoteAgent struct { 42 mu sync.Mutex 43 44 quitCh chan struct{} 45 workCh chan *Work 46 returnCh chan<- *Result 47 48 chain consensus.ChainReader 49 engine consensus.Engine 50 currentWork *Work 51 work map[common.Hash]*Work 52 53 hashrateMu sync.RWMutex 54 hashrate map[common.Hash]hashrate 55 56 running int32 // running indicates whether the agent is active. Call atomically 57 } 58 59 func NewRemoteAgent(chain consensus.ChainReader, engine consensus.Engine) *RemoteAgent { 60 return &RemoteAgent{ 61 chain: chain, 62 engine: engine, 63 work: make(map[common.Hash]*Work), 64 hashrate: make(map[common.Hash]hashrate), 65 } 66 } 67 68 func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) { 69 //a.hashrateMu.Lock() 70 //defer a.hashrateMu.Unlock() 71 72 //a.hashrate[id] = hashrate{time.Now(), rate} 73 } 74 75 func (a *RemoteAgent) Work() chan<- *Work { 76 return a.workCh 77 } 78 79 func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { 80 a.returnCh = returnCh 81 } 82 83 func (a *RemoteAgent) Start() { 84 if !atomic.CompareAndSwapInt32(&a.running, 0, 1) { 85 return 86 } 87 a.quitCh = make(chan struct{}) 88 a.workCh = make(chan *Work, 1) 89 go a.loop(a.workCh, a.quitCh) 90 } 91 92 func (a *RemoteAgent) Stop() { 93 if !atomic.CompareAndSwapInt32(&a.running, 1, 0) { 94 return 95 } 96 close(a.quitCh) 97 close(a.workCh) 98 } 99 100 // GetHashRate returns the accumulated hashrate of all identifier combined 101 func (a *RemoteAgent) GetHashRate() int64 { 102 return 0 103 } 104 105 func (a *RemoteAgent) GetBlockTemplate(coinbaseAddress common.Address) ([]byte, error) { 106 a.mu.Lock() 107 defer a.mu.Unlock() 108 if a.currentWork != nil { 109 if _, ok := a.chain.(*core.BlockChain); !ok { 110 return nil, fmt.Errorf("could not assert interface") 111 } else { 112 hdr := types.CopyHeader(a.currentWork.header) 113 hdr.Coinbase = coinbaseAddress 114 blk := types.NewBlock(hdr, a.currentWork.txs, nil, a.currentWork.receipts) 115 return rlp.EncodeToBytes(blk) 116 } 117 } 118 return nil, errors.New("No work available yet, don't panic.") 119 } 120 121 // SubmitBlock tries to inject a pow solution into the remote agent, returning 122 // whether the solution was accepted or not (not can be both a bad pow as well as 123 // any other error, like no work pending). 124 func (a *RemoteAgent) SubmitBlock(block *types.Block) bool { 125 a.mu.Lock() 126 defer a.mu.Unlock() 127 if block == nil { 128 log.Warn("nil block") 129 return false 130 } 131 if block.Header() == nil { 132 log.Warn("nil block header") 133 return false 134 } 135 if wanted := new(big.Int).Add(a.chain.CurrentHeader().Number, common.Big1); block.Number().Uint64() != wanted.Uint64() { 136 log.Warn("Block submitted out of order", "number", block.Number(), "wanted", wanted) 137 return false 138 } 139 // Make sure the Engine solutions is indeed valid 140 result := block.Header() 141 result.Version = a.chain.Config().GetBlockVersion(result.Number) 142 if result.Version == 0 { 143 log.Warn("Not real work", "version", result.Version) 144 return false 145 } 146 if err := a.engine.VerifyHeader(a.chain, result, true); err != nil { 147 log.Warn("Invalid proof-of-work submitted", "hash", result.Hash(), "number", result.Number, "err", err) 148 return false 149 } 150 // Solutions seems to be valid, return to the miner and notify acceptance 151 a.returnCh <- &Result{nil, block} 152 return true 153 154 } 155 156 func (a *RemoteAgent) GetWork() ([3]string, error) { 157 a.mu.Lock() 158 defer a.mu.Unlock() 159 160 var res [3]string 161 162 if a.currentWork != nil { 163 block := a.currentWork.Block 164 165 res[0] = block.HashNoNonce().Hex() 166 seedHash := aquahash.SeedHash(block.NumberU64(), byte(block.Version())) 167 res[1] = common.BytesToHash(seedHash).Hex() 168 // Calculate the "target" to be returned to the external miner 169 n := big.NewInt(1) 170 n.Lsh(n, 255) 171 n.Div(n, block.Difficulty()) 172 n.Lsh(n, 1) 173 res[2] = common.BytesToHash(n.Bytes()).Hex() 174 175 a.work[block.HashNoNonce()] = a.currentWork 176 return res, nil 177 } 178 return res, errors.New("No work available yet, don't panic.") 179 } 180 181 // SubmitWork tries to inject a pow solution into the remote agent, returning 182 // whether the solution was accepted or not (not can be both a bad pow as well as 183 // any other error, like no work pending). 184 func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { 185 a.mu.Lock() 186 defer a.mu.Unlock() 187 188 // Make sure the work submitted is present 189 work := a.work[hash] 190 if work == nil { 191 log.Info("Work submitted but wasnt pending", "hash", hash) 192 return false 193 } 194 // Make sure the Engine solutions is indeed valid 195 result := work.Block.Header() 196 result.Nonce = nonce 197 result.MixDigest = mixDigest 198 if result.Version == 0 { 199 log.Info("Not real work", "version", result.Version) 200 } 201 if err := a.engine.VerifySeal(a.chain, result); err != nil { 202 log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err) 203 return false 204 } 205 block := work.Block.WithSeal(result) 206 207 // Solutions seems to be valid, return to the miner and notify acceptance 208 a.returnCh <- &Result{work, block} 209 delete(a.work, hash) 210 211 return true 212 } 213 214 // loop monitors mining events on the work and quit channels, updating the internal 215 // state of the remote miner until a termination is requested. 216 // 217 // Note, the reason the work and quit channels are passed as parameters is because 218 // RemoteAgent.Start() constantly recreates these channels, so the loop code cannot 219 // assume data stability in these member fields. 220 func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) { 221 ticker := time.NewTicker(5 * time.Second) 222 defer ticker.Stop() 223 224 for { 225 select { 226 case <-quitCh: 227 return 228 case work := <-workCh: 229 a.mu.Lock() 230 a.currentWork = work 231 a.mu.Unlock() 232 case <-ticker.C: 233 // cleanup 234 a.mu.Lock() 235 for hash, work := range a.work { 236 if time.Since(work.createdAt) > 7*(12*time.Second) { 237 delete(a.work, hash) 238 } 239 } 240 a.mu.Unlock() 241 242 a.hashrateMu.Lock() 243 for id, hashrate := range a.hashrate { 244 if time.Since(hashrate.ping) > 10*time.Second { 245 delete(a.hashrate, id) 246 } 247 } 248 a.hashrateMu.Unlock() 249 } 250 } 251 }