github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/miner/remote_agent.go (about) 1 package miner 2 3 import ( 4 "errors" 5 "math/big" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "github.com/neatlab/neatio/chain/consensus" 11 "github.com/neatlab/neatio/chain/core/types" 12 "github.com/neatlab/neatio/chain/log" 13 "github.com/neatlab/neatio/utilities/common" 14 ) 15 16 type hashrate struct { 17 ping time.Time 18 rate uint64 19 } 20 21 type RemoteAgent struct { 22 mu sync.Mutex 23 24 quitCh chan struct{} 25 workCh chan *Work 26 returnCh chan<- *Result 27 28 chain consensus.ChainReader 29 engine consensus.Engine 30 currentWork *Work 31 work map[common.Hash]*Work 32 33 hashrateMu sync.RWMutex 34 hashrate map[common.Hash]hashrate 35 36 running int32 37 } 38 39 func NewRemoteAgent(chain consensus.ChainReader, engine consensus.Engine) *RemoteAgent { 40 return &RemoteAgent{ 41 chain: chain, 42 engine: engine, 43 work: make(map[common.Hash]*Work), 44 hashrate: make(map[common.Hash]hashrate), 45 } 46 } 47 48 func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) { 49 a.hashrateMu.Lock() 50 defer a.hashrateMu.Unlock() 51 52 a.hashrate[id] = hashrate{time.Now(), rate} 53 } 54 55 func (a *RemoteAgent) Work() chan<- *Work { 56 return a.workCh 57 } 58 59 func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { 60 a.returnCh = returnCh 61 } 62 63 func (a *RemoteAgent) Start() { 64 if !atomic.CompareAndSwapInt32(&a.running, 0, 1) { 65 return 66 } 67 a.quitCh = make(chan struct{}) 68 a.workCh = make(chan *Work, 1) 69 go a.loop(a.workCh, a.quitCh) 70 } 71 72 func (a *RemoteAgent) Stop() { 73 if !atomic.CompareAndSwapInt32(&a.running, 1, 0) { 74 return 75 } 76 close(a.quitCh) 77 close(a.workCh) 78 } 79 80 func (a *RemoteAgent) GetHashRate() (tot int64) { 81 a.hashrateMu.RLock() 82 defer a.hashrateMu.RUnlock() 83 84 for _, hashrate := range a.hashrate { 85 tot += int64(hashrate.rate) 86 } 87 return 88 } 89 90 func (a *RemoteAgent) GetWork() ([3]string, error) { 91 a.mu.Lock() 92 defer a.mu.Unlock() 93 94 var res [3]string 95 96 if a.currentWork != nil { 97 block := a.currentWork.Block 98 99 res[0] = block.HashNoNonce().Hex() 100 101 n := big.NewInt(1) 102 n.Lsh(n, 255) 103 n.Div(n, block.Difficulty()) 104 n.Lsh(n, 1) 105 res[2] = common.BytesToHash(n.Bytes()).Hex() 106 107 a.work[block.HashNoNonce()] = a.currentWork 108 return res, nil 109 } 110 return res, errors.New("No work available yet, don't panic.") 111 } 112 113 func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { 114 a.mu.Lock() 115 defer a.mu.Unlock() 116 117 work := a.work[hash] 118 if work == nil { 119 log.Info("Work submitted but none pending", "hash", hash) 120 return false 121 } 122 123 result := work.Block.Header() 124 result.Nonce = nonce 125 result.MixDigest = mixDigest 126 127 if err := a.engine.VerifySeal(a.chain, result); err != nil { 128 log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err) 129 return false 130 } 131 block := work.Block.WithSeal(result) 132 133 a.returnCh <- &Result{Work: work, Block: block} 134 delete(a.work, hash) 135 136 return true 137 } 138 139 func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) { 140 ticker := time.NewTicker(5 * time.Second) 141 defer ticker.Stop() 142 143 for { 144 select { 145 case <-quitCh: 146 return 147 case work := <-workCh: 148 a.mu.Lock() 149 a.currentWork = work 150 a.mu.Unlock() 151 case <-ticker.C: 152 153 a.mu.Lock() 154 for hash, work := range a.work { 155 if time.Since(work.createdAt) > 7*(12*time.Second) { 156 delete(a.work, hash) 157 } 158 } 159 a.mu.Unlock() 160 161 a.hashrateMu.Lock() 162 for id, hashrate := range a.hashrate { 163 if time.Since(hashrate.ping) > 10*time.Second { 164 delete(a.hashrate, id) 165 } 166 } 167 a.hashrateMu.Unlock() 168 } 169 } 170 }