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