remote_agent.go 5.2 KB

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