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