github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/miner/remote_agent.go (about)

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