remote_agent.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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/logger"
  26. "github.com/ethereum/go-ethereum/logger/glog"
  27. )
  28. type hashrate struct {
  29. ping time.Time
  30. rate uint64
  31. }
  32. type RemoteAgent struct {
  33. mu sync.Mutex
  34. quit chan struct{}
  35. workCh chan *Work
  36. returnCh chan<- *Result
  37. currentWork *Work
  38. work map[common.Hash]*Work
  39. hashrateMu sync.RWMutex
  40. hashrate map[common.Hash]hashrate
  41. running int32 // running indicates whether the agent is active. Call atomically
  42. }
  43. func NewRemoteAgent() *RemoteAgent {
  44. return &RemoteAgent{
  45. work: make(map[common.Hash]*Work),
  46. hashrate: make(map[common.Hash]hashrate),
  47. }
  48. }
  49. func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) {
  50. a.hashrateMu.Lock()
  51. defer a.hashrateMu.Unlock()
  52. a.hashrate[id] = hashrate{time.Now(), rate}
  53. }
  54. func (a *RemoteAgent) Work() chan<- *Work {
  55. return a.workCh
  56. }
  57. func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) {
  58. a.returnCh = returnCh
  59. }
  60. func (a *RemoteAgent) Start() {
  61. if !atomic.CompareAndSwapInt32(&a.running, 0, 1) {
  62. return
  63. }
  64. a.quit = make(chan struct{})
  65. a.workCh = make(chan *Work, 1)
  66. go a.maintainLoop()
  67. }
  68. func (a *RemoteAgent) Stop() {
  69. if !atomic.CompareAndSwapInt32(&a.running, 1, 0) {
  70. return
  71. }
  72. close(a.quit)
  73. close(a.workCh)
  74. }
  75. // GetHashRate returns the accumulated hashrate of all identifier combined
  76. func (a *RemoteAgent) GetHashRate() (tot int64) {
  77. a.hashrateMu.RLock()
  78. defer a.hashrateMu.RUnlock()
  79. // this could overflow
  80. for _, hashrate := range a.hashrate {
  81. tot += int64(hashrate.rate)
  82. }
  83. return
  84. }
  85. func (a *RemoteAgent) GetWork() ([3]string, error) {
  86. a.mu.Lock()
  87. defer a.mu.Unlock()
  88. var res [3]string
  89. if a.currentWork != nil {
  90. block := a.currentWork.Block
  91. res[0] = block.HashNoNonce().Hex()
  92. seedHash, _ := ethash.GetSeedHash(block.NumberU64())
  93. res[1] = common.BytesToHash(seedHash).Hex()
  94. // Calculate the "target" to be returned to the external miner
  95. n := big.NewInt(1)
  96. n.Lsh(n, 255)
  97. n.Div(n, block.Difficulty())
  98. n.Lsh(n, 1)
  99. res[2] = common.BytesToHash(n.Bytes()).Hex()
  100. a.work[block.HashNoNonce()] = a.currentWork
  101. return res, nil
  102. }
  103. return res, errors.New("No work available yet, don't panic.")
  104. }
  105. // Returns true or false, but does not indicate if the PoW was correct
  106. func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, hash common.Hash) bool {
  107. a.mu.Lock()
  108. defer a.mu.Unlock()
  109. // Make sure the work submitted is present
  110. if a.work[hash] != nil {
  111. block := a.work[hash].Block.WithMiningResult(nonce, mixDigest)
  112. a.returnCh <- &Result{a.work[hash], block}
  113. delete(a.work, hash)
  114. return true
  115. } else {
  116. glog.V(logger.Info).Infof("Work was submitted for %x but no pending work found\n", hash)
  117. }
  118. return false
  119. }
  120. func (a *RemoteAgent) maintainLoop() {
  121. ticker := time.Tick(5 * time.Second)
  122. out:
  123. for {
  124. select {
  125. case <-a.quit:
  126. break out
  127. case work := <-a.workCh:
  128. a.mu.Lock()
  129. a.currentWork = work
  130. a.mu.Unlock()
  131. case <-ticker:
  132. // cleanup
  133. a.mu.Lock()
  134. for hash, work := range a.work {
  135. if time.Since(work.createdAt) > 7*(12*time.Second) {
  136. delete(a.work, hash)
  137. }
  138. }
  139. a.mu.Unlock()
  140. a.hashrateMu.Lock()
  141. for id, hashrate := range a.hashrate {
  142. if time.Since(hashrate.ping) > 10*time.Second {
  143. delete(a.hashrate, id)
  144. }
  145. }
  146. a.hashrateMu.Unlock()
  147. }
  148. }
  149. }