remote_agent.go 5.0 KB

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