github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/miner/remote_agent.go (about)

     1  package miner
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/neatlab/neatio/chain/consensus"
    11  	"github.com/neatlab/neatio/chain/core/types"
    12  	"github.com/neatlab/neatio/chain/log"
    13  	"github.com/neatlab/neatio/utilities/common"
    14  )
    15  
    16  type hashrate struct {
    17  	ping time.Time
    18  	rate uint64
    19  }
    20  
    21  type RemoteAgent struct {
    22  	mu sync.Mutex
    23  
    24  	quitCh   chan struct{}
    25  	workCh   chan *Work
    26  	returnCh chan<- *Result
    27  
    28  	chain       consensus.ChainReader
    29  	engine      consensus.Engine
    30  	currentWork *Work
    31  	work        map[common.Hash]*Work
    32  
    33  	hashrateMu sync.RWMutex
    34  	hashrate   map[common.Hash]hashrate
    35  
    36  	running int32
    37  }
    38  
    39  func NewRemoteAgent(chain consensus.ChainReader, engine consensus.Engine) *RemoteAgent {
    40  	return &RemoteAgent{
    41  		chain:    chain,
    42  		engine:   engine,
    43  		work:     make(map[common.Hash]*Work),
    44  		hashrate: make(map[common.Hash]hashrate),
    45  	}
    46  }
    47  
    48  func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) {
    49  	a.hashrateMu.Lock()
    50  	defer a.hashrateMu.Unlock()
    51  
    52  	a.hashrate[id] = hashrate{time.Now(), rate}
    53  }
    54  
    55  func (a *RemoteAgent) Work() chan<- *Work {
    56  	return a.workCh
    57  }
    58  
    59  func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) {
    60  	a.returnCh = returnCh
    61  }
    62  
    63  func (a *RemoteAgent) Start() {
    64  	if !atomic.CompareAndSwapInt32(&a.running, 0, 1) {
    65  		return
    66  	}
    67  	a.quitCh = make(chan struct{})
    68  	a.workCh = make(chan *Work, 1)
    69  	go a.loop(a.workCh, a.quitCh)
    70  }
    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  
    80  func (a *RemoteAgent) GetHashRate() (tot int64) {
    81  	a.hashrateMu.RLock()
    82  	defer a.hashrateMu.RUnlock()
    83  
    84  	for _, hashrate := range a.hashrate {
    85  		tot += int64(hashrate.rate)
    86  	}
    87  	return
    88  }
    89  
    90  func (a *RemoteAgent) GetWork() ([3]string, error) {
    91  	a.mu.Lock()
    92  	defer a.mu.Unlock()
    93  
    94  	var res [3]string
    95  
    96  	if a.currentWork != nil {
    97  		block := a.currentWork.Block
    98  
    99  		res[0] = block.HashNoNonce().Hex()
   100  
   101  		n := big.NewInt(1)
   102  		n.Lsh(n, 255)
   103  		n.Div(n, block.Difficulty())
   104  		n.Lsh(n, 1)
   105  		res[2] = common.BytesToHash(n.Bytes()).Hex()
   106  
   107  		a.work[block.HashNoNonce()] = a.currentWork
   108  		return res, nil
   109  	}
   110  	return res, errors.New("No work available yet, don't panic.")
   111  }
   112  
   113  func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool {
   114  	a.mu.Lock()
   115  	defer a.mu.Unlock()
   116  
   117  	work := a.work[hash]
   118  	if work == nil {
   119  		log.Info("Work submitted but none pending", "hash", hash)
   120  		return false
   121  	}
   122  
   123  	result := work.Block.Header()
   124  	result.Nonce = nonce
   125  	result.MixDigest = mixDigest
   126  
   127  	if err := a.engine.VerifySeal(a.chain, result); err != nil {
   128  		log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err)
   129  		return false
   130  	}
   131  	block := work.Block.WithSeal(result)
   132  
   133  	a.returnCh <- &Result{Work: work, Block: block}
   134  	delete(a.work, hash)
   135  
   136  	return true
   137  }
   138  
   139  func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) {
   140  	ticker := time.NewTicker(5 * time.Second)
   141  	defer ticker.Stop()
   142  
   143  	for {
   144  		select {
   145  		case <-quitCh:
   146  			return
   147  		case work := <-workCh:
   148  			a.mu.Lock()
   149  			a.currentWork = work
   150  			a.mu.Unlock()
   151  		case <-ticker.C:
   152  
   153  			a.mu.Lock()
   154  			for hash, work := range a.work {
   155  				if time.Since(work.createdAt) > 7*(12*time.Second) {
   156  					delete(a.work, hash)
   157  				}
   158  			}
   159  			a.mu.Unlock()
   160  
   161  			a.hashrateMu.Lock()
   162  			for id, hashrate := range a.hashrate {
   163  				if time.Since(hashrate.ping) > 10*time.Second {
   164  					delete(a.hashrate, id)
   165  				}
   166  			}
   167  			a.hashrateMu.Unlock()
   168  		}
   169  	}
   170  }